Tornado是非阻塞式web服务器,另外,Tornado还是一个支持WebSocket的优秀框架
`pip3 install tornado`
快速开始
#!/usr/bin/env python # -*- coding:utf-8 -*- import tornado.ioloop import tornado.web class MainHandler(tornado.web.RequestHandler): def get(self): self.write("Hello, world") application = tornado.web.Application([ (r"/index", MainHandler), ]) if __name__ == "__main__": application.listen(8888) tornado.ioloop.IOLoop.instance().start()
Tornado是一个非阻塞的web服务器以及web框架,但是在使用的时候只有使用异步的库才会真正发挥它异步的优势,因为异步的库返回的是Future对象
在3.0版本之后,gen.coroutine模块显得比较突出。coroutine装饰器可以让本来靠回调的异步编程看起来像同步编程。
其中便是利用了Python中生成器的Send函数。在生成器中,yield关键字往往会与正常函数中的return相比。它可以被当成迭代器,从而使用next()返回yield的结果。
但是生成器还有另外一个用法,就是使用send方法。在生成器内部可以将yield的结果赋值给一个变量,而这个值是通过外部的生成器client来send的
import tornado.ioloop import tornado.web from tornado.httpclient import AsyncHTTPClient from tornado import gen class MainHandler(tornado.web.RequestHandler): @gen.coroutine def get(self): client = AsyncHTTPClient() resp = yield client.fetch("https://www.baidu.com") # 必须用异步的模块 if resp.code == 200: self.write(str(resp)) else: self.write("Fail") application = tornado.web.Application([ (r"/index", MainHandler), ]) if __name__ == "__main__": application.listen(8009) tornado.ioloop.IOLoop.instance().start()
路由系统
路由系统其实就是 url 和 类 的对应关系,这里不同于其他框架,其他很多框架均是 url 对应 函数,Tornado中每个url对应的是一个类。
#!/usr/bin/env python # -*- coding:utf-8 -*- 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("buy.wupeiqi.com/index") application = tornado.web.Application([ (r"/index", MainHandler), (r"/story/([0-9]+)", StoryHandler), ]) application.add_handlers('buy.wupeiqi.com$', [ (r'/index',BuyHandler), ]) if __name__ == "__main__": application.listen(80) tornado.ioloop.IOLoop.instance().start()
静态文件
settings = { 'template_path': 'template', # 定义模板根目录 'static_path': 'jingtai', # 定义静态文件根目录,使用:"/static/xx.jpg" 'static_url_prefix': '/jt/', # 使用:"/jt/xx.jpg" }
模板引擎
class MainHandler(tornado.web.RequestHandler): def get(self): self.render("index.html", list_info = [11,22,33], k='a1')
渲染变量:{{ }}
条件语句:{% if len(items) > 2 %} ... {% else %} .... {% end %}
循环:{% for item in list_info %} {{ item }} {% end %}
css、js渲染:
<link href="{{static_url("css/common.css")}}" rel="stylesheet" />
<script src="{{static_url("js/jquery-1.8.2.min.js")}}"></script>
其他方法:
在模板中默认提供了一些函数、字段、类以供模板使用: escape: tornado.escape.xhtml_escape 的別名 xhtml_escape: tornado.escape.xhtml_escape 的別名 url_escape: tornado.escape.url_escape 的別名 json_encode: tornado.escape.json_encode 的別名 squeeze: tornado.escape.squeeze 的別名 linkify: tornado.escape.linkify 的別名 datetime: Python 的 datetime 模组 handler: 当前的 RequestHandler 对象 request: handler.request 的別名 current_user: handler.current_user 的別名 locale: handler.locale 的別名 _: handler.locale.translate 的別名 static_url: for handler.static_url 的別名 xsrf_form_html: handler.xsrf_form_html 的別名
模板复用:
header.html
<div> <ul> <li>USA</li> <li>51区</li> </ul> </div>
使用:{% include 'header.html' %}
模板继承:
############## layout.html ################# <!DOCTYPE html> <html> <head> .... {% block CSS %}{% end %} .... </head> <body> ...... {% block RenderBody %} {% end %} ....... {% block JavaScript %}{% end %} </body> </html>
{% extends 'layout.html'%} {% block CSS %} <link href="{{static_url("css/index.css")}}" rel="stylesheet" /> {% end %} {% block RenderBody %} <h1>Index</h1> <ul> {% for item in li %} <li>{{item}}</li> {% end %} </ul> {% end %} {% block JavaScript %} .. {% end %}
自定义UIMethod以UIModule
类似于django的自定义标签(定义-注册-使用)
from tornado.web import UIModule from tornado import escape """1. 定义""" ################ uimodules.py ############### class custom(UIModule): def render(self, *args, **kwargs): return escape.xhtml_escape('<h1>wupeiqi</h1>') ############################################ ################ uimethod.py ############### def tab(self): return 'UIMethod' ############################################ """2. 注册""" import uimodules as md import uimethods as mt settings = { 'ui_methods': mt, 'ui_modules': md, } """3. 使用""" ''' {% module custom(123) %} {{ tab() }} '''
CSRF
settings = { "xsrf_cookies": True, } application = tornado.web.Application([ (r"/", MainHandler), (r"/login", LoginHandler), ], **settings)
使用:普通表单
<form action="/new_message" method="post"> {{ xsrf_form_html() }} <input type="text" name="message"/> <input type="submit" value="Post"/> </form>
使用:ajax
function getCookie(name) { var r = document.cookie.match("\\b" + name + "=([^;]*)\\b"); return r ? r[1] : undefined; } jQuery.postJSON = function(url, args, callback) { args._xsrf = getCookie("_xsrf"); $.ajax({url: url, data: $.param(args), dataType: "text", type: "POST", success: function(response) { callback(eval("(" + response + ")")); }}); };
cookie
class MainHandler(tornado.web.RequestHandler): def get(self): if not self.get_cookie("mycookie"): self.set_cookie("mycookie", "myvalue") self.write("Your cookie was not set yet!") else: self.write("Your cookie was set!")
cookie加密
Cookie 很容易被恶意的客户端伪造。加入你想在 cookie 中保存当前登陆用户的 id 之类的信息,你需要对 cookie 作签名以防止伪造。
Tornado 通过 set_secure_cookie 和 get_secure_cookie 方法直接支持了这种功能。 要使用这些方法,你需要在创建应用时提供一个密钥,名字为 cookie_secret。 你可以把它作为一个关键词参数传入应用的设置中:
class MainHandler(tornado.web.RequestHandler): def get(self): if not self.get_secure_cookie("mycookie"): self.set_secure_cookie("mycookie", "myvalue") self.write("Your cookie was not set yet!") else: self.write("Your cookie was set!") application = tornado.web.Application([ (r"/", MainHandler), ], cookie_secret="61oETzKXQAGaYdkL5gEmGeJJFuYh7EQnp2XdTP1o/Vo=")
签名Cookie的本质是:
写cookie过程:
- 将值进行base64加密
- 对除值以外的内容进行签名,哈希算法(无法逆向解析)
- 拼接 签名 + 加密值
读cookie过程:
- 读取 签名 + 加密值
- 对签名进行验证
- base64解密,获取值内容
form表单+iframe实现局部刷新
11