Application 是生成Tornado实例的类,它的构造函数接收很多关于tornado应用的配置参数,不仅仅是之前的路由映射列表,包括生产模式、是否自动重启等
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('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.web.RequestHandler为我们提供了许多方法来获取请求的信息
name
default
strip
get_query_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
若在正则表达式中定义了名字,则参数按名传递
若未定义名字,则参数按顺序传递
提取出来的参数会作为对应请求方法函数的参数
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()
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)
手动设置一个名为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")
在进入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)
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 |
重定向到某个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() # 开启服务
抛出HTTP错误状态码status_code,默认为500,kwargs为可变命名参数
class IndexHandler(RequestHandler):
def get(self):
self.write("主页")
self.send_error(404, content="出现404错误")
处理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() # 开启服务
路由映射中的第三个字典型参数会作为该方法的命名参数传递
class HelloHandler(RequestHandler):
def initialize(self, database):
self.database = database
app = Application([
(r'/', HelloHandler, dict(database=database)),
])
预处理,在执行对应请求方式的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
在请求处理结束后调用,通常该方法用来进行资源清理释放或处理日志等
在正常情况未抛出错误时,调用顺序为:
set_defautl_headers()
initialize()
prepare()
HTTP方法
on_finish()
在有错误抛出时,调用顺序为:
set_default_headers()
initialize()
prepare()
HTTP方法
set_default_headers()
write_error()
on_finish()