二、Tornado的输入与输出


Application

Application 是生成Tornado实例的类,它的构造函数接收很多关于tornado应用的配置参数,不仅仅是之前的路由映射列表,包括生产模式、是否自动重启等

settings

debug
设置tornado是否工作在调试模式,默认为False即工作在生产模式

自动重启
tornado应用会监控我们的源代码文件,当有改动保存后便会重启程序
这一特性也可单独通过autoreload=True设置

if __name__ == '__main__':
    app = tornado.web.Application([
        (r'/', HelloHandler),  # 生成服务实例,通常包含路由信息
    ],
            # debug=True,
            autoreload=True,  # debug 与 autoreload 效果一致
    )
    app.listen(8000)  # 监听端口
    tornado.ioloop.IOLoop.current().start()  # 开启服务
路由映射

一个基本路由映射表的格式是很简单的,传入两个参数,分别是url及其对应的Handler

[(r"/", HelloHandler),]

实际上,我们还可以在路由映射列表中,给路由传入多个信息,以便在Handler中取出这些参数使用,例如

[
    (r"/", HelloHandler),
    (r"/test", TestHandler, {"subject":"c++"}),
    url(r"/python", TestHandler, {"subject":"python"}, name="python_url")
]

在上面简单的例子中,我们在一些路由中传入了一个字典,当我们需要在Handler中将这些参数提取出来使用,则需要在对应Handler的initialize()方法获取

import tornado.web
import tornado.ioloop
import tornado.httpserver
import tornado.options

tornado.options.define("port", default=8000, type=int, help="define port")


class HelloHandler(tornado.web.RequestHandler):
    def get(self):
        self.write("Hello world")


class TestHandler(tornado.web.RequestHandler):
    def initialize(self, subject, score):
        # 将参数取出
        self.subject = subject
        self.score = score

    def get(self):
        self.write(self.subject)
        self.write(str(self.score))


if __name__ == '__main__':
    tornado.options.parse_command_line()
    app = tornado.web.Application([
        (r'/', HelloHandler),
        (r'/test', TestHandler, {"subject": "c++", "score": 90})  # 定义TestHandler的参数
    ], debug=True
    )
    app.listen(tornado.options.options.port)
    tornado.ioloop.IOLoop.current().start()

在这里插入图片描述

在最开始的路由参数例子中,注意到/test/python两个url对应的都是TestHandler,且还多了一个name参数,是为了说明路由的重定向

路由中的name字段使用tornado.web.url来构建,name是路由的名字,>RequestHandler.reverse_url(name)可以获取该名字对应的url

import tornado.web
import tornado.ioloop
import tornado.httpserver
import tornado.options

tornado.options.define("port", default=8000, type=int, help="define port")


class HelloHandler(tornado.web.RequestHandler):
    def get(self):
        url = self.reverse_url("python_url")  # url=127.0.0.1:8000/python
        self.write('<a href="%s">python_url</a>' % url)  # 生成一个简单的连接,访问


class TestHandler(tornado.web.RequestHandler):
    def initialize(self, subject):
        self.subject = subject

    def get(self):
        self.write(self.subject)  # 根据请求的url取得不同的subject值


if __name__ == '__main__':
    tornado.options.parse_command_line()
    app = tornado.web.Application([
        (r'/', HelloHandler),
        (r'/test', TestHandler, {"subject": "c++"}),  # /test传到TestHandler中的subject参数是c++
        tornado.web.url(r'/python', TestHandler, {"subject": "python"}, name="python_url"),
        # 即/python的name=python_url,/python传到TestHandler中的subject参数是python
    ], debug=True
    )
    app.listen(tornado.options.options.port)
    tornado.ioloop.IOLoop.current().start()

Tornado的输入

tornado.web.RequestHandler为我们提供了许多方法来获取请求的信息

获取查询字符串参数
  • get_query_argument(name, default=_ARG_DEFAULT, strip=True)
    name
    从请求的查询字符串中返回指定参数的值,如果出现多个同名参数,则返回最后一个的值
    default
    为设值未传name参数时返回的默认值,未设置且未传值则抛tornado.web.MissingArgumentError异常
    strip
    表示是否过滤掉左右两边的空白字符,默认为过滤
    get_query_arguments(name, strip=True)
    从请求的查询字符串中返回指定参数name的值,返回list列表(即使name参数只有一个值)
获取请求体参数
  • get_body_argument(name, default=_ARG_DEFAULT, strip=True)
  • get_body_arguments(name, strip=True)
    使用方法如查询字符串,不再赘述
同时获得两种请求参数
  • get_argument(name, default=_ARG_DEFAULT, strip=True)
  • get_arguments(name, strip=True)
    使用方法如查询字符串,不再赘述
import tornado.web
import tornado.ioloop


class HelloHandler(tornado.web.RequestHandler):
    def get(self):
        self.name = self.get_argument("name", "")  # 获取参数,默认空字符串
        self.age = self.get_argument("age", "")
        output = "my name is " + self.name + " and my age is " + self.age
        self.write(output)


if __name__ == '__main__':
    app = tornado.web.Application([
        (r'/', HelloHandler),  # 生成服务实例,通常包含路由信息
    ])
    app.listen(8000)  # 监听端口
    tornado.ioloop.IOLoop.current().start()  # 开启服务

在这里插入图片描述

其他请求信息

RequestHandler.request 对象还存储了每个请求的相关信息:
method:HTTP的请求方式,如GET或POST;
host:被请求的主机名;
uri:请求的完整资源标示,包括路径和查询字符串;
path:请求的路径部分;
query:请求的查询字符串部分;
version:使用的HTTP版本;
headers:请求的协议头,是类字典型的对象,支持关键字索引的方式获取特定协议头信息,例如:request.headers[“Content-Type”]
body:请求体数据;
remote_ip:客户端的IP地址;

class HelloHandler(tornado.web.RequestHandler):
    def get(self):
        print(self.request.headers)

>>>Host: 127.0.0.1:8000
>>>Connection: keep-alive
>>>Cache-Control: max-age=0
>>>Upgrade-Insecure-Requests: 1
>>>User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36
>>>Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3
>>>Accept-Encoding: gzip, deflate, br
>>>Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
正则提取uri

若在正则表达式中定义了名字,则参数按名传递
若未定义名字,则参数按顺序传递
提取出来的参数会作为对应请求方法函数的参数

import tornado.web
import tornado.ioloop
import tornado.httpserver
import tornado.options

tornado.options.define("port", default=8000, type=int, help="define port")


class HelloHandler(tornado.web.RequestHandler):
    def get(self):
        self.write("hello world")


class TestHandler(tornado.web.RequestHandler):
    def get(self, subject, city):  # 按顺序传递需要注意参数的顺序,避免混乱
        self.write(("Subject: %s<br/>City: %s" % (subject, city)))


class PythonHandler(tornado.web.RequestHandler):
    def get(self, city, subject):  # 这里将顺序写反,但因为按名称传递,也能够正确获得url中的参数
        self.write(("Subject: %s<br/>City: %s" % (subject, city)))


if __name__ == '__main__':
    tornado.options.parse_command_line()
    app = tornado.web.Application([
        (r'/', HelloHandler),
        (r'/test/(.+)/([a-z]+)', TestHandler),  # 没有定义名称,因此按顺序传递
        (r'/python/(?P<subject>.+)/(?P<city>[a-z]+)', PythonHandler),  # 定义了名称,因此按名称传递
    ])
    http_server = tornado.httpserver.HTTPServer(app)
    http_server.listen(tornado.options.options.port)
    tornado.ioloop.IOLoop.current().start()

Tornado的输出

self.write(chunk)

write方法是写到缓冲区的,多次使用write方法不断追加响应内容,最终所有写到缓冲区的内容一起作为本次请求的响应输出

class IndexHandler(RequestHandler):
    def get(self):
        self.write("hello world 1!")
        self.write("hello world 2!")
        self.write("hello world 3!")

write方法检测到传入的chunk参数是字典类型会自动转换为json字符串(Content-Type:application/json)

class IndexHandler(RequestHandler):
    def get(self):
        stu = {
            "name":"zhangsan",
            "age":24,
            "gender":1,
        }
        self.write(stu)
self.set_header(name, value)

手动设置一个名为name、值为value的响应头header字段

import json

class IndexHandler(RequestHandler):
    def get(self):
        stu = {
            "name":"zhangsan",
            "age":24,
            "gender":1,
        }
        stu_json = json.dumps(stu)
        self.write(stu_json)
        self.set_header("Content-Type", "application/json; charset=UTF-8")
set_default_headers()

在进入HTTP处理方法前先被调用,可以重写此方法来预先设置默认的headers
使用set_header()方法会覆盖掉在set_default_headers()方法中设置的同名header

class IndexHandler(RequestHandler):
    def set_default_headers(self):
        self.set_header("Content-Type", "application/json; charset=UTF-8")
        self.set_header("test_header", "python")

    def get(self):
        stu = {
            "name":"zhangsan",
            "age":24,
            "gender":1,
        }
        stu_json = json.dumps(stu)
        self.write(stu_json)
        self.set_header("test_header", "i love python") # 注意此处重写了header中的test_header字段

    def post(self):
        stu = {
            "name":"zhangsan",
            "age":24,
            "gender":1,
        }
        stu_json = json.dumps(stu)
        self.write(stu_json)
self.set_status(status_code, reason=None)

status_code:int类型,状态码,若reason为None,则状态码必须为下表中的内容
reason:string类型,描述状态码的词组,若为None,则会被自动填充为下表中的内容

CodeEnum NameDetails
100CONTINUEHTTP/1.1 RFC 7231, Section 6.2.1
101SWITCHING_PROTOCOLSHTTP/1.1 RFC 7231, Section 6.2.2
102PROCESSINGWebDAV RFC 2518, Section 10.1
200OKHTTP/1.1 RFC 7231, Section 6.3.1
201CREATEDHTTP/1.1 RFC 7231, Section 6.3.2
202ACCEPTEDHTTP/1.1 RFC 7231, Section 6.3.3
203NON_AUTHORITATIVE_INFORMATIONHTTP/1.1 RFC 7231, Section 6.3.4
204NO_CONTENTHTTP/1.1 RFC 7231, Section 6.3.5
205RESET_CONTENTHTTP/1.1 RFC 7231, Section 6.3.6
206PARTIAL_CONTENTHTTP/1.1 RFC 7233, Section 4.1
207MULTI_STATUSWebDAV RFC 4918, Section 11.1
208ALREADY_REPORTEDWebDAV Binding Extensions RFC 5842, Section 7.1 (Experimental)
226IM_USEDDelta Encoding in HTTP RFC 3229, Section 10.4.1
300MULTIPLE_CHOICESHTTP/1.1 RFC 7231, Section 6.4.1
301MOVED_PERMANENTLYHTTP/1.1 RFC 7231, Section 6.4.2
302FOUNDHTTP/1.1 RFC 7231, Section 6.4.3
303SEE_OTHERHTTP/1.1 RFC 7231, Section 6.4.4
304NOT_MODIFIEDHTTP/1.1 RFC 7232, Section 4.1
305USE_PROXYHTTP/1.1 RFC 7231, Section 6.4.5
307TEMPORARY_REDIRECTHTTP/1.1 RFC 7231, Section 6.4.7
308PERMANENT_REDIRECTPermanent Redirect RFC 7238, Section 3 (Experimental)
400BAD_REQUESTHTTP/1.1 RFC 7231, Section 6.5.1
401UNAUTHORIZEDHTTP/1.1 Authentication RFC 7235, Section 3.1
402PAYMENT_REQUIREDHTTP/1.1 RFC 7231, Section 6.5.2
403FORBIDDENHTTP/1.1 RFC 7231, Section 6.5.3
404NOT_FOUNDHTTP/1.1 RFC 7231, Section 6.5.4
405METHOD_NOT_ALLOWEDHTTP/1.1 RFC 7231, Section 6.5.5
406NOT_ACCEPTABLEHTTP/1.1 RFC 7231, Section 6.5.6
407PROXY_AUTHENTICATION_REQUIREDHTTP/1.1 Authentication RFC 7235, Section 3.2
408REQUEST_TIMEOUTHTTP/1.1 RFC 7231, Section 6.5.7
409CONFLICTHTTP/1.1 RFC 7231, Section 6.5.8
410GONEHTTP/1.1 RFC 7231, Section 6.5.9
411LENGTH_REQUIREDHTTP/1.1 RFC 7231, Section 6.5.10
412PRECONDITION_FAILEDHTTP/1.1 RFC 7232, Section 4.2
413REQUEST_ENTITY_TOO_LARGEHTTP/1.1 RFC 7231, Section 6.5.11
414REQUEST_URI_TOO_LONGHTTP/1.1 RFC 7231, Section 6.5.12
415UNSUPPORTED_MEDIA_TYPEHTTP/1.1 RFC 7231, Section 6.5.13
416REQUEST_RANGE_NOT_SATISFIABLEHTTP/1.1 Range Requests RFC 7233, Section 4.4
417EXPECTATION_FAILEDHTTP/1.1 RFC 7231, Section 6.5.14
422UNPROCESSABLE_ENTITYWebDAV RFC 4918, Section 11.2
423LOCKEDWebDAV RFC 4918, Section 11.3
424FAILED_DEPENDENCYWebDAV RFC 4918, Section 11.4
426UPGRADE_REQUIREDHTTP/1.1 RFC 7231, Section 6.5.15
428PRECONDITION_REQUIREDAdditional HTTP Status Codes RFC 6585
429TOO_MANY_REQUESTSAdditional HTTP Status Codes RFC 6585
431REQUEST_HEADER_FIELDS_TOO_LARGE AdditionalHTTP Status Codes RFC 6585
500INTERNAL_SERVER_ERRORHTTP/1.1 RFC 7231, Section 6.6.1
501NOT_IMPLEMENTEDHTTP/1.1 RFC 7231, Section 6.6.2
502BAD_GATEWAYHTTP/1.1 RFC 7231, Section 6.6.3
503SERVICE_UNAVAILABLEHTTP/1.1 RFC 7231, Section 6.6.4
504GATEWAY_TIMEOUTHTTP/1.1 RFC 7231, Section 6.6.5
505HTTP_VERSION_NOT_SUPPORTEDHTTP/1.1 RFC 7231, Section 6.6.6
506VARIANT_ALSO_NEGOTIATESTransparent Content Negotiation in HTTP RFC 2295, Section 8.1 (Experimental)
507INSUFFICIENT_STORAGEWebDAV RFC 4918, Section 11.5
508LOOP_DETECTEDWebDAV Binding Extensions RFC 5842, Section 7.2 (Experimental)
510NOT_EXTENDEDAn HTTP Extension Framework RFC 2774, Section 7 (Experimental)
511NETWORK_AUTHENTICATION_REQUIREDAdditional HTTP Status Codes RFC 6585, Section 6
redirect(url)

重定向到某个url下

import tornado.web
import tornado.ioloop


class IndexHandler(tornado.web.RequestHandler):
    def get(self):
        self.write("主页")


class LoginHandler(tornado.web.RequestHandler):
    def get(self):
        self.write('<form method="post"><input type="submit" value="登陆"></form>')

    def post(self):
        self.redirect("/")


if __name__ == '__main__':
    app = tornado.web.Application([
        (r'/', IndexHandler),  # 生成服务实例,通常包含路由信息
        (r'/login', LoginHandler)
    ], debug=True)
    app.listen(8000)  # 监听端口
    tornado.ioloop.IOLoop.current().start()  # 开启服务
send_error(status_code=500, **kwargs)

抛出HTTP错误状态码status_code,默认为500,kwargs为可变命名参数

class IndexHandler(RequestHandler):
    def get(self):
        self.write("主页")
        self.send_error(404, content="出现404错误")
write_error(status_code, **kwargs)

处理send_error抛出的错误信息并返回给浏览器错误信息页面
重写此方可以定制自己的错误显示页面

import tornado.web
import tornado.ioloop


class IndexHandler(tornado.web.RequestHandler):
    def get(self):
        err_code = self.get_argument("code", None)
        err_title = self.get_argument("title", "")
        err_content = self.get_argument("content", "")
        if err_code:
            self.send_error(int(err_code), title=err_title, content=err_content)
        else:
            self.write("主页")

    def write_error(self, status_code, **kwargs):
        self.write(u"<h1>出错,需要尽快解决!</h1>")
        self.write(u"<p>error_name:%s</p>" % kwargs["title"])
        self.write(u"<p>error_detail:%s</p>" % kwargs["content"])


if __name__ == '__main__':
    app = tornado.web.Application([
        (r'/', IndexHandler),  # 生成服务实例,通常包含路由信息\
    ], debug=True)
    app.listen(8000)  # 监听端口
    tornado.ioloop.IOLoop.current().start()  # 开启服务

方法与调用顺序

initialize()

路由映射中的第三个字典型参数会作为该方法的命名参数传递

class HelloHandler(RequestHandler):
    def initialize(self, database):
        self.database = database

app = Application([
    (r'/', HelloHandler, dict(database=database)),
    ])
prepare()

预处理,在执行对应请求方式的HTTP方法前先执行
不论以何种HTTP方式请求,都会执行prepare()方法

import json

class HelloHandler(RequestHandler):
    def prepare(self):
        if self.request.headers.get("Content-Type").startswith("application/json"):
            self.json_dict = json.loads(self.request.body)
        else:
            self.json_dict = None
on_finish()

在请求处理结束后调用,通常该方法用来进行资源清理释放或处理日志等

调用顺序

在正常情况未抛出错误时,调用顺序为:

set_defautl_headers()
initialize()
prepare()
HTTP方法
on_finish()

在有错误抛出时,调用顺序为:

set_default_headers()
initialize()
prepare()
HTTP方法
set_default_headers()
write_error()
on_finish()

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值