Tornado
一、Tornado 入门
1、前戏
(1) 什么是 Tornado
Tornado 是一个基于Python的Web服务框架和 异步网络库, 最早开发与 FriendFeed 公司. 通过利用非阻塞网络 I/O, Tornado 可以承载成千上万的活动连接, 完美的实现了 长连接WebSockets) 和其他对于每一位用户来说需要长连接的程序.
Tornado和现在的主流 Web 框架(包括大多数python框架)有着明显的区别:
他是非阻塞式服务器,而且速度相当快,的利于其非阻塞的方式和对 epoll 的运用,Tornado每秒可以处理数以千计的连接,因此Tornado是实时 Web 服务的一个理想框架,我们开发这个 Web 服务器的主要目的就是问了处理 FrienFeed 的实时功能——在 FrienFeed 的应用里每一个活动用户都会保持着一个服务器连接。
(2) Tornado 应用场景
-
用户量大,高并发
如秒杀抢购,双十一某宝购物,春节抢火车票
-
大量的HTTP持久连接
使用同一个TCP连接来发送和接收多个HTTP请求/应答,而不是为了每一个新的请求/应答打开新的连接的方法
对于HTTP1.0可以在请求的包头(Header)中添加Connection: Keep-Alive
对于HTTP1.1所有的连接默认都是持久连接
-
对于这两种场景,通常基于多线程的服务器
(3) C10K
上面的高并发问题,通常C10K这一概念来描述。C10K-Conncurrently handing ten thousand connections, 即并发10000个连接。对于单台服务器而言,根本无法承担,而采用多台服务器分布式有意味着高昂的成本
Tornado在设计之初就考虑到了性能因素,旨在解决C10K问题,这样设计使得其成为一个拥有非常高性能的解决方案(服务器与框架的集合体)
(4) Tornado&Django
4.1 Djando
- 注重高效开发
- 全自动化的管理后台(只需要使用ORM,做简单的定义,就能自动生成数据库结构,全功能的管理平台)
- session功能
4.2 Tornado
- 注重性能优越,速度快
- 解决高并发
- 异步非阻塞
- websockets 长链接
- 内嵌HTTP服务器
- 单线程的异步网络程序,默认启动时根据CPU数量运行多个实例;利用CPU多核的优势。
(5) Epoll
epoll是Linux内核为处理大批量文件描述符而作了改进的poll,是Linux下多路复用IO接口select/poll的增强版本,它能显著提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率。另一点原因就是获取事件的时候,它无须遍历整个被侦听的描述符集,只要遍历那些被内核IO事件异步唤醒而加入Ready队列的描述符集合就行了。epoll除了提供select/poll那种IO事件的水平触发(Level Triggered)外,还提供了边缘触发(Edge Triggered),这就使得用户空间程序有可能缓存IO状态,减少epoll_wait/epoll_pwait的调用,提高应用程序效率。
(6) Tornado 安装
pip install tornado
2、第一个 Tornado 代码
(1) 方式一
import tornado.web
import tornado.ioloop
# 视图
class HelloHandler(tornado.web.RequestHandler):
def get(self, *args, **kwargs):
self.write('hello word')
# 路由
app = tornado.web.Application([
(r'/hello', HelloHandler)
])
# 绑定端口
app.listen(8888)
# 启动监听
tornado.ioloop.IOLoop.instance().start()
(2) 方式二
import tornado.web
import tornado.ioloop
# 视图
class HelloHandler(tornado.web.RequestHandler):
# 接收GET请求
def get(self):
self.write("Hello word!")
# 路由
app = tornado.web.Application([
(r"/hello", HelloHandler),
])
if __name__ == '__main__':
# 绑定端口
app.listen(8888)
# 启动监听
tornado.ioloop.IOLoop.current().start()
(3) 方式三(官方)
import tornado.ioloop
import tornado.web
class HelloHandler(tornado.web.RequestHandler):
def get(self):
self.write("Hello, world")
def make_app():
return tornado.web.Application([
(r"/hello", HelloHandler),
])
if __name__ == "__main__":
app = make_app()
app.listen(8888)
tornado.ioloop.IOLoop.current().start()
(4) 代码执行流程
- 第一步:执行脚本,监听 8888 端口
- 第二步:浏览器客户端访问 /hello --> http://127.0.0.1:8888/hello
- 第三步:服务器接受请求,并交由对应的类处理该请求
- 第四步:类接受到请求之后,根据请求方式(post / get / delete ...)的不同调用并执行相应的方法
- 第五步:方法返回值的字符串内容发送浏览器
二、请求与响应
1、httpserver
一个非阻塞的单线程 HTTP 服务器。
(1) listen:简单的单进程
import tornado.ioloop
import tornado.web
import tornado.httpserver
class HelloHandler(tornado.web.RequestHandler):
def get(self):
self.write("Hello, world")
def make_app():
return tornado.web.Application([
(r"/hello", HelloHandler),
])
if __name__ == "__main__":
app = make_app()
server = tornado.httpserver.HTTPServer(app)
server.listen(8888)
tornado.ioloop.IOLoop.current().start()
(2) bind / start:简单的多进程
import tornado.ioloop
import tornado.web
import tornado.httpserver
class HelloHandler(tornado.web.RequestHandler):
def get(self):
self.write("Hello, world")
def make_app():
return tornado.web.Application([
(r"/hello", HelloHandler),
])
if __name__ == "__main__":
app = make_app()
server = tornado.httpserver.HTTPServer(app)
server.bind(8888)
server.start(1)
tornado.ioloop.IOLoop.current().start()
(3) add_sockets:先进的多进程
# 注意 windows 上不支持
import tornado.ioloop
import tornado.web
import tornado.httpserver
import tornado.netutil
import tornado.process
class HelloHandler(tornado.web.RequestHandler):
def get(self):
self.write("Hello, world")
def make_app():
return tornado.web.Application([
(r"/hello", HelloHandler),
])
if __name__ == "__main__":
app = make_app()
sockets = tornado.netutil.bind_sockets(8888)
tornado.process.fork_processes(0)
server = tornado.httpserver.HTTPServer(app)
server.add_sockets(sockets)
tornado.ioloop.IOLoop.current().start()
2、options
3、路由
路由系统其实就是 url 和 类 的对应关系,这里不同于其他框架,其他很多框架均是 url 对应 函数,Tornado中每个url对应的是一个类。
import tornado.ioloop
import tornado.web
class MainHandler(tornado.web.RequestHandler):
def get(self):
self.write("Hello, world")
class StoryHandler(tornado.web.RequestHandler):
def get(self, story_id):
self.write("You requested the story " + story_id)
class BuyHandler(tornado.web.RequestHandler):
def get(self):
self.write("www.xingxing.com")
# 默认路由系统, 根据url的不容调用不同的类
application = tornado.web.Application([
(r"/index", MainHandler),
(r"/story/([0-9]+)", StoryHandler),
])
# 二级路由匹配
application.add_handlers('www.xingxing.com$', [
(r'/index',BuyHandler),
])
if __name__ == "__main__":
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()
3、请求和响应
3.1 请求相关
(1) 获取查询字符串参数
app.py
import tornado.web
import tornado.ioloop
import tornado.options
# 定义处理类
class LoginHandler(tornado.web.RequestHandler):
# 接收GET请求
def get(self, *args, **kwargs):
"""
语法:get_query_argument(name, default='', strip=True)
参数:
name: 参数名,如果出现多个同名参数,则返回最后一个的值
default: 默认值,为路由中未传name参数时返回的默认值,如default未设置,则会抛出tornado.web.MissingArgumentError异常;
strip: 是否过滤掉左右两边的空白字符,默认为过滤。
返回值:字符串
语法:get_query_arguments(name, strip=True)
从请求的查询字符串中返回指定参数name的值,注意返回的是list列表(即使对应name参数只有一个值),若未找到name参数,则返回空列表。
返回值:列表
"""
print(self.get_query_argument('name', default='', strip=True))
print(self.get_query_argument('pwd', default='', strip=True))
print(self.get_query_arguments('name', strip=True))
print(self.get_query_arguments('pwd', strip=True))
self.render("templates/login.html")
# 设置路由
application = tornado.web.Application([
(r"/login", LoginHandler),
])
if __name__ == '__main__':
# 打印请求信息(日志信息),不加这一行后端不会有日志信息
tornado.options.parse_command_line()
# 绑定端口
application.listen(8888)
# 启动监听
tornado.ioloop.IOLoop.current().start()
templates/login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/login" method="get">
<p>
用户名:<input type="text" name="name">
</p>
<p>
密码:<input type="password" name="pwd">
</p>
<p>
<input type="submit" value="登录">
</p>
</form>
</body>
</html>
(2) 获取请求体数据
app.py
import tornado.web
import tornado.ioloop
import tornado.options
# 定义处理类
class LoginHandler(tornado.web.RequestHandler):
# 接收GET请求
def get(self, *args, **kwargs):
self.render("templates/login.html")
def post(self, *args, **kwargs):
"""
get_body_argument(name, default=_ARG_DEFAULT, strip=True)
从请求体中返回指定参数name的值,如出现多个同名参数,则返回最后一个的值;
default与strip同上。
get_body_arguments(name, strip=True)
从请求体中返回指定参数name的值,注意返回的是list列表(即使对应name参数只有一个值),若未找到name参数,则返回空列表。
说明:对于请求体中数据为json或xml的,无法通过这两个方法获取。
"""
print(self.get_body_argument('name', default='', strip=True))
print(self.get_body_argument('pwd', default='', strip=True))
print(self.get_body_arguments('name', strip=True))
print(self.get_body_arguments('pwd', strip=True))
self.write("登录成功")
# 设置路由
application = tornado.web.Application([
(r"/login", LoginHandler),
])
if __name__ == '__main__':
# 打印请求信息(日志信息),不加这一行后端不会有日志信息
tornado.options.parse_command_line()
# 绑定端口
application.listen(8888)
# 启动监听
tornado.ioloop.IOLoop.current().start()
templates/login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/login" method="post">
<p>
用户名:<input type="text" name="name">
</p>
<p>
密码:<input type="password" name="pwd">
</p>
<p>
<input type="submit" value="登录">
</p>
</form>
</body>
</html>
(3) 前两类方法的整合
app.py
import tornado.web
import tornado.ioloop
import tornado.options
# 定义处理类
class LoginHandler(tornado.web.RequestHandler):
# 接收GET请求
def get(self, *args, **kwargs):
self.render("templates/login.html")
def post(self, *args, **kwargs):
"""
get_argument(name, default=_ARG_DEFAULT, strip=True)
get_arguments(name, strip=True)
说明:对于请求体中数据为json或xml的,无法通过这两个方法获取。
这两个方法最常用
"""
print(self.get_argument('name', default='', strip=True))
print(self.get_argument('pwd', default='', strip=True))
print(self.get_arguments('name', strip=True))
print(self.get_arguments('pwd', strip=True))
self.write("登录成功")
# 设置路由
application = tornado.web.Application([
(r"/login", LoginHandler),
])
if __name__ == '__main__':
# 打印请求信息(日志信息),不加这一行后端不会有日志信息
tornado.options.parse_command_line()
# 绑定端口
application.listen(8888)
# 启动监听
tornado.ioloop.IOLoop.current().start()
templates/login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/login" method="post">
<p>
用户名:<input type="text" name="name">
</p>
<p>
密码:<input type="password" name="pwd">
</p>
<p>
<input type="submit" value="登录">
</p>
</form>
</body>
</html>
(4) Tornado的Request对象属性
属性 | 说明 |
---|---|
self.request.method | http的请求头;get, post等 |
self.reuqest.uri | 客户端请求完整的uri |
self.request.path | uri的路径名,不包含查询的字符串 |
self.request.query | uri中的查询字符串 |
self.request.version | http/1.1 |
self.request.headers | 请求头 |
self.requset.body | 字符串的消息 |
self.request.remote_ip | 客户端请求的ip |
self.request.protocol | http的协议http or https |
self.request.host | 请求消息的主机名 |
self.request.files | 以字典的方式表达客户端上传的文件 |
self.request.cookies | 客户端的cookies字典 |
self.request.arguments | 客户端提交的参数 |
import tornado.web
import tornado.ioloop
import tornado.options
# 定义处理类
class LoginHandler(tornado.web.RequestHandler):
# 接收GET请求
def get(self, *args, **kwargs):
print("\n-------method:\n", self.request.method)
print("\n-------uri:\n", self.request.uri)
print("\n-------path:\n", self.request.path)
print("\n-------query:\n", self.request.query)
print("\n-------version:\n", self.request.version)
print("\n-------headers:\n", self.request.headers)
print("\n-------body:\n", self.request.body)
print("\n-------remote_ip:\n", self.request.remote_ip)
print("\n-------protocol:\n", self.request.protocol)
print("\n-------host:\n", self.request.host)
print("\n-------arguments:\n", self.request.arguments)
print("\n-------query_arguments:\n", self.request.query_arguments)
print("\n-------body_arguments:\n", self.request.body_arguments)
print("\n-------files:\n", self.request.files)
print("\n-------cookies:\n", self.request.cookies)
self.render("templates/login.html")
# 设置路由
app = tornado.web.Application([
(r"/login", LoginHandler
if __name__ == '__main__':
# 打印请求信息(日志信息),不加这一行后端不会有日志信息
tornado.options.parse_command_line()
# 绑定端口
app.listen(8888)
# 启动监听
tornado.ioloop.IOLoop.current().start()
(5) 文件上传
app.py
import os
import json
import tornado.web
import tornado.ioloop
import tornado.options
class UploadHandler(tornado.web.RequestHandler):
# 接收GET请求
def get(self):
self.render('templates/upload.html')
def post(self, *args):
ret = {'result': 'OK'}
file_metas = self.request.files["img"] # 获取文件信息
for meta in file_metas:
file_name = meta['filename'] # 获取它的文件名
with open(os.path.join("static", "upload", file_name), 'wb') as up: # 打开本地一个文件
up.write(meta['body']) # body就是文件内容,把它写到本地
self.write(json.dumps(ret))
# 设置路由
app = tornado.web.Application([
(r"/upload", UploadHandler)
])
if __name__ == '__main__':
tornado.options.parse_command_line()
app.listen(8888)
tornado.ioloop.IOLoop.current().start()
templates/upload.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>上传文件</title>
</head>
<body>
<form method="post" action="/upload" enctype="multipart/form-data">
<input type="file" name="img">
<input type="submit" value="上传">
</form>
</body>
</html>
3.2 响应相关
(1) write(chunk)
作用:将chunk数据写到输出缓冲区,我们可以在同一个视图中多次使用write方法, 对应的响应socket将缓冲区的数据返回给浏览器。
缓冲区刷新条件:1.程序结束;2.手动刷新;3.缓冲区满了;遇到/n
import json
import tornado.web
import tornado.ioloop
import tornado.options
# 返回普通文本
class TextHandler(tornado.web.RequestHandler):
# 接收GET请求
def get(self):
self.write("Hello world!")
# 返回json数据
class JsonHandler(tornado.web.RequestHandler):
"""
实际上,我们可以不用自己手动去做json序列化,当write方法检测到传入的chunk参数是字典类型后,会自动转换成json字符串,
并在响应头header中设置Content-Type: application/json;charset=UTF-8
自己手动序列化时为:Content-Type: text/html; charset=UTF-8
"""
# 接收GET请求
def get(self):
data = {
"name": "Tornado",
"age": 20,
"type": "Python Web 框架"
}
data_json = json.dumps(data)
self.write(data_json)
# 设置路由
app = tornado.web.Application([
(r"/text", TextHandler),
(r"/json", JsonHandler),
])
if __name__ == '__main__':
# 打印请求信息(日志信息),不加这一行后端不会有日志信息
tornado.options.parse_command_line()
# 绑定端口
app.listen(8888)
# 启动监听
tornado.ioloop.IOLoop.current().start()
(2) finish(chunck=None)
作用:刷新缓冲区,并关闭当次请求通道。
因此,self.finish()后面的self.write('asdf')内容虽然会写入到缓冲区,但是请求通道已关闭,不会返回给用户,没有意义了。
import tornado.web
import tornado.ioloop
import tornado.options
class IndexHandler(tornado.web.RequestHandler):
def get(self):
self.finish("Hello world!")
self.write("Hello world!") # 这一行不会返回给用户
# 设置路由
app = tornado.web.Application([
(r"/index", IndexHandler)
])
if __name__ == '__main__':
# 打印请求信息(日志信息),不加这一行后端不会有日志信息
tornado.options.parse_command_line()
# 绑定端口
app.listen(8888)
# 启动监听
tornado.ioloop.IOLoop.current().start()
(3) *_header(name, value)
作用:如,手动设置一个名为username,值为xxxx的响应头字段
参数:name,字段名称;value字段值
import json
import tornado.web
import tornado.ioloop
import tornado.options
# 定义处理类
class IndexHandler(tornado.web.RequestHandler):
# 接收GET请求
def get(self):
data = {
"name": "Tornado",
"age": 20,
"type": "Python Web 框架"
}
data_json = json.dumps(data)
self.write(data_json)
# 设置header
self.set_header("Content-Type", "application/json;charset=UTF-8")
self.set_header("Tornado", "xxxxxxxxx")
# 添加header
self.add_header("Django", "zzzzzzzzzz")
# 清除键为Django的响应头
self.clear_header("Django")
# 设置路由
app = tornado.web.Application([
(r"/index", IndexHandler)
])
if __name__ == '__main__':
tornado.options.parse_command_line()
app.listen(8888)
tornado.ioloop.IOLoop.current().start()
(4) set_default_headers()
通常,要修改响应头,重写此方法,而不是在随处任写;在此方法中,调用self.set_header()去设置响应头。
作用:此方法,在进入HTTP响应处理之前调用,可以重写该 方法来预先设置响应头。
注意:如果在多处,写了self.set_header()方法;后调用的方法会覆盖先调用的方法。
import json
import tornado.web
import tornado.ioloop
import tornado.options
# 定义处理类
class IndexHandler(tornado.web.RequestHandler):
def set_default_headers(self):
self.set_header("Content-Type", "application/json;charset=UTF-8")
self.set_header("Tornado", "xxxxxxxxx")
# 接收GET请求
def get(self):
data = {
"name": "Tornado",
"age": 20,
"type": "Python Web 框架"
}
data_json = json.dumps(data)
self.write(data_json)
# 设置路由
app = tornado.web.Application([
(r"/index", IndexHandler)
])
if __name__ == '__main__':
# 打印请求信息(日志信息),不加这一行后端不会有日志信息
tornado.options.parse_command_line()
# 绑定端口
app.listen(8888)
# 启动监听
tornado.ioloop.IOLoop.current().start()
(5) set_status(status_code, reson=None)
作用:为响应设置状态码
参数:status_code,状态码值,为int整型;reason,描述状态码的词组,string类型。
如果reason的值为None,则状态码必须为系统定义的值。
状态码 | 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 |
import json
import tornado.web
import tornado.ioloop
import tornado.options
# 使用已有的(系统定义的)状态码:
class Demo1Handler(tornado.web.RequestHandler):
def get(self):
data = {
"name": "Tornado",
"age": 20,
"type": "Python Web 框架"
}
data_json = json.dumps(data)
self.write(data_json)
self.set_status(404)
# 使用自定义的状态码:此时,reason不能为None,否则将抛出异常。
class Demo2Handler(tornado.web.RequestHandler):
def get(self):
data = {
"name": "Tornado",
"age": 20,
"type": "Python Web 框架"
}
data_json = json.dumps(data)
self.write(data_json)
self.set_status(999, '原因')
# 设置路由
app = tornado.web.Application([
(r"/demo_1", Demo1Handler),
(r"/demo_2", Demo2Handler)
])
if __name__ == '__main__':
tornado.options.parse_command_line()
app.listen(8888)
tornado.ioloop.IOLoop.current().start()
(6) redirect(url)
作用:重定向到url网址
import json
import tornado.web
import tornado.ioloop
import tornado.options
# 登录
class LoginHandler(tornado.web.RequestHandler):
def get(self):
self.redirect('/index')
# 首页
class IndexHandler(tornado.web.RequestHandler):
# 接收GET请求
def get(self):
data = {
"name": "Tornado",
"age": 20,
"type": "Python Web 框架"
}
data_json = json.dumps(data)
self.write(data_json)
# 设置路由
app = tornado.web.Application([
(r"/login", LoginHandler),
(r"/index", IndexHandler)
])
if __name__ == '__main__':
# 打印请求信息(日志信息),不加这一行后端不会有日志信息
tornado.options.parse_command_line()
# 绑定端口
app.listen(8888)
# 启动监听
tornado.ioloop.IOLoop.current().start()
(7) send_error(status_code=500, **kwargs)
作用:抛出HTTP错误状态码,默认为500。抛出错误后tornado会调用writer_error()方法进行处理,并返回给浏览器错误界面。
import tornado.web
import tornado.ioloop
import tornado.options
# 首页
class IndexHandler(tornado.web.RequestHandler):
# 接收GET请求
def get(self):
self.send_error(404)
# 设置路由
app = tornado.web.Application([
(r"/index", IndexHandler)
])
if __name__ == '__main__':
tornado.options.parse_command_line()
app.listen(8888)
tornado.ioloop.IOLoop.current().start()
(8) write_error(status_code, **kwargs)
作用:用来处理send_error抛出的错误信息,并并返回给浏览器错误界面。同上面的方法一起使用。
import tornado.web
import tornado.ioloop
import tornado.options
class IndexHandler(tornado.web.RequestHandler):
def write_error(self, status_code, **kwargs):
if status_code == 500:
self.set_status(500)
# 或者返回自定义的页面
self.write("服务器内部错误")
elif status_code == 404:
self.set_status(404)
self.write("资源不存在")
else:
self.set_status(999, "what")
self.write("未知错误")
# 接收GET请求
def get(self):
self.send_error(500)
# 设置路由
app = tornado.web.Application([
(r"/index", IndexHandler)
])
if __name__ == '__main__':
tornado.options.parse_command_line()
app.listen(8888)
tornado.ioloop.IOLoop.current().start()
(9) render
app.py
import tornado.web
import tornado.ioloop
import tornado.options
# 首页
class IndexHandler(tornado.web.RequestHandler):
# 接收GET请求
def get(self):
self.render('templates/index.html')
# 设置路由
app = tornado.web.Application([
(r"/index", IndexHandler)
])
if __name__ == '__main__':
tornado.options.parse_command_line()
app.listen(8888)
tornado.ioloop.IOLoop.current().start()
templates/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>首页页面</title>
</head>
<body>
<h1>好好学习 Tornado</h1>
</body>
</html>
4、模板
Tornao中的模板语言和django中类似,模板引擎将模板文件载入内存,然后将数据嵌入其中,最终获取到一个完整的字符串,再将字符串返回给请求者。
Tornado 的模板支持“控制语句”和“表达语句”,控制语句是使用 {% 和 %} 包起来的 例如 {% if len(items) > 2 %}。表达语句是使用 {{ 和 }} 包起来的,例如 {{ items[0] }}。
控制语句和对应的 Python 语句的格式基本完全相同。我们支持 if、for、while 和 try,这些语句逻辑结束的位置需要用 {% end %} 做标记。还通过 extends 和 block 语句实现了模板继承。这些在 template 模块的代码文档中有着详细的描述。
未完待续