二、Tornado的输入与输出

文章目录

    • Application
        • settings
        • 路由映射
    • Tornado的输入
        • 获取查询字符串参数
        • 获取请求体参数
        • 同时获得两种请求参数
        • 其他请求信息
        • 正则提取uri
    • Tornado的输出
        • self.write(chunk)
        • self.set_header(name, value)
        • set_default_headers()
        • self.set_status(status_code, reason=None)
        • redirect(url)
        • send_error(status_code=500, **kwargs)
        • write_error(status_code, **kwargs)
    • 方法与调用顺序
        • initialize()
        • prepare()
        • on_finish()
        • 调用顺序


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()

二、Tornado的输入与输出_第1张图片

在最开始的路由参数例子中,注意到/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('python_url' % 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()  # 开启服务

二、Tornado的输入与输出_第2张图片

其他请求信息

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
City: %s"
% (subject, city))) class PythonHandler(tornado.web.RequestHandler): def get(self, city, subject): # 这里将顺序写反,但因为按名称传递,也能够正确获得url中的参数 self.write(("Subject: %s
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.+)/(?P[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,则会被自动填充为下表中的内容

Code Enum Name Details
100 CONTINUE HTTP/1.1 RFC 7231, Section 6.2.1
101 SWITCHING_PROTOCOLS HTTP/1.1 RFC 7231, Section 6.2.2
102 PROCESSING WebDAV RFC 2518, Section 10.1
200 OK HTTP/1.1 RFC 7231, Section 6.3.1
201 CREATED HTTP/1.1 RFC 7231, Section 6.3.2
202 ACCEPTED HTTP/1.1 RFC 7231, Section 6.3.3
203 NON_AUTHORITATIVE_INFORMATION HTTP/1.1 RFC 7231, Section 6.3.4
204 NO_CONTENT HTTP/1.1 RFC 7231, Section 6.3.5
205 RESET_CONTENT HTTP/1.1 RFC 7231, Section 6.3.6
206 PARTIAL_CONTENT HTTP/1.1 RFC 7233, Section 4.1
207 MULTI_STATUS WebDAV RFC 4918, Section 11.1
208 ALREADY_REPORTED WebDAV Binding Extensions RFC 5842, Section 7.1 (Experimental)
226 IM_USED Delta Encoding in HTTP RFC 3229, Section 10.4.1
300 MULTIPLE_CHOICES HTTP/1.1 RFC 7231, Section 6.4.1
301 MOVED_PERMANENTLY HTTP/1.1 RFC 7231, Section 6.4.2
302 FOUND HTTP/1.1 RFC 7231, Section 6.4.3
303 SEE_OTHER HTTP/1.1 RFC 7231, Section 6.4.4
304 NOT_MODIFIED HTTP/1.1 RFC 7232, Section 4.1
305 USE_PROXY HTTP/1.1 RFC 7231, Section 6.4.5
307 TEMPORARY_REDIRECT HTTP/1.1 RFC 7231, Section 6.4.7
308 PERMANENT_REDIRECT Permanent Redirect RFC 7238, Section 3 (Experimental)
400 BAD_REQUEST HTTP/1.1 RFC 7231, Section 6.5.1
401 UNAUTHORIZED HTTP/1.1 Authentication RFC 7235, Section 3.1
402 PAYMENT_REQUIRED HTTP/1.1 RFC 7231, Section 6.5.2
403 FORBIDDEN HTTP/1.1 RFC 7231, Section 6.5.3
404 NOT_FOUND HTTP/1.1 RFC 7231, Section 6.5.4
405 METHOD_NOT_ALLOWED HTTP/1.1 RFC 7231, Section 6.5.5
406 NOT_ACCEPTABLE HTTP/1.1 RFC 7231, Section 6.5.6
407 PROXY_AUTHENTICATION_REQUIRED HTTP/1.1 Authentication RFC 7235, Section 3.2
408 REQUEST_TIMEOUT HTTP/1.1 RFC 7231, Section 6.5.7
409 CONFLICT HTTP/1.1 RFC 7231, Section 6.5.8
410 GONE HTTP/1.1 RFC 7231, Section 6.5.9
411 LENGTH_REQUIRED HTTP/1.1 RFC 7231, Section 6.5.10
412 PRECONDITION_FAILED HTTP/1.1 RFC 7232, Section 4.2
413 REQUEST_ENTITY_TOO_LARGE HTTP/1.1 RFC 7231, Section 6.5.11
414 REQUEST_URI_TOO_LONG HTTP/1.1 RFC 7231, Section 6.5.12
415 UNSUPPORTED_MEDIA_TYPE HTTP/1.1 RFC 7231, Section 6.5.13
416 REQUEST_RANGE_NOT_SATISFIABLE HTTP/1.1 Range Requests RFC 7233, Section 4.4
417 EXPECTATION_FAILED HTTP/1.1 RFC 7231, Section 6.5.14
422 UNPROCESSABLE_ENTITY WebDAV RFC 4918, Section 11.2
423 LOCKED WebDAV RFC 4918, Section 11.3
424 FAILED_DEPENDENCY WebDAV RFC 4918, Section 11.4
426 UPGRADE_REQUIRED HTTP/1.1 RFC 7231, Section 6.5.15
428 PRECONDITION_REQUIRED Additional HTTP Status Codes RFC 6585
429 TOO_MANY_REQUESTS Additional HTTP Status Codes RFC 6585
431 REQUEST_HEADER_FIELDS_TOO_LARGE Additional HTTP Status Codes RFC 6585
500 INTERNAL_SERVER_ERROR HTTP/1.1 RFC 7231, Section 6.6.1
501 NOT_IMPLEMENTED HTTP/1.1 RFC 7231, Section 6.6.2
502 BAD_GATEWAY HTTP/1.1 RFC 7231, Section 6.6.3
503 SERVICE_UNAVAILABLE HTTP/1.1 RFC 7231, Section 6.6.4
504 GATEWAY_TIMEOUT HTTP/1.1 RFC 7231, Section 6.6.5
505 HTTP_VERSION_NOT_SUPPORTED HTTP/1.1 RFC 7231, Section 6.6.6
506 VARIANT_ALSO_NEGOTIATES Transparent Content Negotiation in HTTP RFC 2295, Section 8.1 (Experimental)
507 INSUFFICIENT_STORAGE WebDAV RFC 4918, Section 11.5
508 LOOP_DETECTED WebDAV Binding Extensions RFC 5842, Section 7.2 (Experimental)
510 NOT_EXTENDED An HTTP Extension Framework RFC 2774, Section 7 (Experimental)
511 NETWORK_AUTHENTICATION_REQUIRED Additional 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('
'
) 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"

出错,需要尽快解决!

"
) self.write(u"

error_name:%s

"
% kwargs["title"]) self.write(u"

error_detail:%s

"
% 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()

你可能感兴趣的:(Tornado)