Back to Blogs
tornado
web
python

Tornado Web Framework

Soloman
2020-01-07

Tornado Web Framework

1 基本使用

import json
import os

import tornado.ioloop
import tornado.web

BASE_DIR = os.path.dirname(__file__)


settings = {
    "template_path": os.path.join(BASE_DIR, "templates"),  # 存放模板的文件夹 html
    "static_path": os.path.join(BASE_DIR, "static"),  # 存放静态文件的文件夹 css js
    "debug": True,
    "autoreload": True,
    "cookie_secret": "TozDU7esgEUTqQy0M6c5II8==",  # 安全cookie
    "xsrf_cookies": False,
}


class IndexHandler(tornado.web.RequestHandler):
    def get(self):
        result = {"hello": "world"}
        # self.render("index.html")
        self.write(json.dumps(result))


if __name__ == '__main__':
    application = tornado.web.Application(
        [
            (r"/index", IndexHandler),
        ],
        **settings)
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()

2 常规配置

debug或者autoreloadTrue时,修改源文件代码将会自动重启服务,相当于Django的重启功能

设置项描述
autoreload如果True,任何源文件更改时服务器进程将重新启动,如调试模式和自动重新加载中所述。此选项是Tornado 3.2中的新选项; 以前此功能由debug设置控制
debug几种调试模式设置的简写,在调试模式和自动重新加载中描述。设置debug=True相当于autoreload=True,compiled_template_cache=False,static_hash_cache=False,serve_traceback=True
default_handler_class|default_handler_args如果没有找到其他匹配项,将使用此处理程序; 使用它来实现自定义404页面(Tornado 3.2中的新增功能)
compress_response如果True,文本格式的响应将自动压缩。Tornado 4.0的新功能
gzipcompress_response自Tornado 4.0以来已弃用的别名
log_function此函数将在每个记录结果的请求结束时调用(使用一个参数,即RequestHandler对象)。默认实现将写入logging模块的根记录器。也可以通过覆盖来定制Application.log_request。可以自定制日志的输出格式
serve_traceback如果True,默认错误页面将包含错误的回溯。此选项是Tornado 3.2中的新选项; 以前此功能由debug设置控制
ui_modules | ui_methods可以设置为UIModule可用于模板的映射或UI方法。可以设置为模块,字典或模块和/或dicts列表。有关详细信息,请参阅UI模块
websocket_ping_interval如果设置为数字,则每n秒钟将对所有websockets进行ping操作。这有助于通过关闭空闲连接的某些代理服务器保持连接活动,并且它可以检测websocket是否在未正确关闭的情况下发生故障。
websocket_ping_timeout如果设置了ping间隔,并且服务器在这么多秒内没有收到“pong”,它将关闭websocket。默认值是ping间隔的三倍,最少30秒。如果未设置ping间隔,则忽略。

3 身份/验证/安全

设置项描述
cookie_secret用于RequestHandler.get_secure_cookieset_secure_cookie签署cookie
key_versionset_secure_cooki 当cookie_secret是密钥字典时,requestHandler 使用特定密钥对cookie进行签名
login_urlauthenticated如果用户未登录,装饰器将重定向到此URL。可以通过覆盖进一步自定义RequestHandler.get_login_url
xsrf_cookies如果True,将启用跨站点请求伪造保护
xsrf_cookie_version控制此服务器生成的新XSRF cookie的版本。通常应该保留默认值(它始终是支持的最高版本),但可以在版本转换期间临时设置为较低的值。Tornado 3.2.2中的新功能,它引入了XSRF cookie版本2
xsrf_cookie_kwargs可以设置为要传递给RequestHandler.set_cookie XSRF cookie 的其他参数的字典
twitter_consumer_key所用的 tornado.auth模块来验证各种API,如检测这些种类账号是否登录等...
twitter_consumer_secret同上..
friendfeed_consumer_key同上..
friendfeed_consumer_secret同上..
google_consumer_key同上..
google_consumer_secret同上..
facebook_api_key同上..
facebook_secret同上..

4 模板设置

设置项描述
autoescape控制模板的自动转义。可以设置为None禁用转义,或者设置 应该传递所有输出的函数的名称。默认为"xhtml_escape"。可以使用该指令在每个模板的基础上进行更改。% autoescape %
compiled_template_cache默认是True; 如果False每个请求都会重新编译模板。此选项是Tornado 3.2中的新选项; 以前此功能由debug设置控制
template_path包含模板文件的目录。可以通过覆盖进一步定制RequestHandler.get_template_path
template_loader分配给tornado.template.BaseLoader自定义模板加载的实例 。如果使用此 设置,则忽略template_path和autoescape设置。可以通过覆盖进一步定制RequestHandler.create_template_loader
template_whitespace控制模板中空格的处理; 查看tornado.template.filter_whitespace允许的值。Tornado 4.3中的新功能

5 静态文件

设置项描述
static_hash_cache默认是True; 如果False 每次请求都会重新计算静态网址。此选项是Tornado 3.2中的新选项; 以前此功能由debug设置控制
static_path将从中提供静态文件的目录
static_url_prefix静态文件的Url前缀,默认为/static/
static_handler_class | static_handler_args可以设置为静态文件而不是默认文件使用不同的处理程序 tornado.web.StaticFileHandler。 static_handler_args如果设置,则应该是要传递给处理程序initialize方法的关键字参数的字典。

6 路由和参数匹配

tornado中,一个url对应一个处理类。

6.1 无名分组

使用正则分组()解析出资源请求地址的一些参数。匹配到的参数会通过位置传参的形式传递给控制器处理函数,所以接收参数可以任意命名,因此你可以通过 *args 接收到所有匹配的参数

import tornado.ioloop
import tornado.web

settings = {"debug": True}
# http://127.0.0.1:8888/register1/soloman
# http://127.0.0.1:8888/register2/soloman


class RegisterHandler1(tornado.web.RequestHandler):
    def get(self, *args):
        self.write(str(args))  # ('soloman',)

        
class RegisterHandler2(tornado.web.RequestHandler):
    def get(self, params):
        self.write(params)   # soloman


application = tornado.web.Application([
    (r"^/register1/(\w+)", RegisterHandler1),
    (r"^/register2/(\w+)", RegisterHandler2),
], **settings)


if __name__ == '__main__':
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()

6.2 有名分组

使用正则的有名分组(?P<组名>规则)解析出资源请求地址的一些参数。匹配到的参数会通过关键字传参的形式传递给控制器处理函数,所以接收参数必须与组名相同,因此你可以通过**kwargs接收到所有匹配的参数

import tornado.ioloop
import tornado.web

settings = {"debug": True}
# http://127.0.0.1:8888/register1/soloman
# http://127.0.0.1:8888/register2/soloman


class RegisterHandler1(tornado.web.RequestHandler):
    def get(self, **kwargs):
        self.write(str(kwargs))  # {'username': 'soloman'}

        
class RegisterHandler2(tornado.web.RequestHandler):
    def get(self, username):
        self.write(username)  # soloman


application = tornado.web.Application([
    (r"^/register1/(?P<username>\w+)", RegisterHandler1),
    (r"^/register2/(?P<username>\w+)", RegisterHandler2),
], **settings)


if __name__ == '__main__':
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()

注意:路由匹配的参数捕捉,不允许无名分组和有名分组的混合使用,这会引发一个异常

6.3 反向解析

反向解析要与路由命名配合使用

import tornado.ioloop
import tornado.web

settings = {"debug": True}
# http://127.0.0.1:8888/register


class RegisterHandler(tornado.web.RequestHandler):
    def get(self):
        print(self.reverse_url("reg"))  # /register
        self.write("注册页面")


# 使用tornado.web.url来进行添加路由规则
application = tornado.web.Application([
    tornado.web.url(r'/register', RegisterHandler, name="reg")
], **settings)


if __name__ == '__main__':
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()

7 请求处理类

tornado中,请求处理类都继承了一个叫做tornado.web.RequestHandler的类。以下是常用的获取相关属性以及方法

属性/方法描述
self.request获取用户请求相关信息
self._headers获取请求头信息,基本请求头被过滤
self.request.headers获取请求头信息,包含基本请求头
slef.request.body获取请求体信息,bytes格式
self.request.remote_ip获取客户端IP
self.request.method获取请求方式
self.request.version获取所使用的HTTP版本
self.get_query_argument()获取单个GET中传递过来的参数,如果多个参数同名,获取最后一个
slef.get_query_arguments()获取所有GET中传递过来的参数,返回列表的形式
self.get_body_argument()获取单个POST中传递过来的参数,如果多个参数同名,获取最后一个
self.get_body_arguments()获取所有POST中传递过来的参数,返回列表的形式
self.get_argument()获取单个GET/POST中传递过来的参数,如果多个参数同名,获取最后一个
self.get_arguments()获取所有GET/POST中传递过来的参数,返回列表的形式
self.request.files获取所有通过 multipart/form-data POST 请求上传的文件
self.request.host获取主机名
self.request.uri获取请求的完整资源标识,包括路径和查询字符串
self.request.query获取查询字符串的部分
self.request.path获取请求的路径( ?之前的所有内容)

8 HTTP 响应

响应一般就分为以下几种,返回单纯的字符串,返回一个模板页面,返回JSON格式数据,返回一个错误,以及重定向

self.write("OK")

self.render("templatePath", **kwargs)  # 传递给模板的数据

import json
json_data = json.dumps({"k1": "v1"})
self.write(json_data)

self.send_error(404) 
self.finish()  # 结束本次请求

self.redirect("www.soloman.vip", status=301)

# 设置响应头
self.set_header("k1", 1)
self.add_header("k2", 2)
self.clear_header("k1") 

在响应处理类中定义钩子函数

class APIHandler(tornado.web.RequestHandler):
    def set_default_headers(self):
        print("first--设置headers")

    def initialize(self):
        print("second--初始化")

    def prepare(self):
        print("third--准备工作")

    def get(self):
        print("fourth--处理get请求")

    def post(self):
        print('fourth--处理post请求')

    def write_error(self, status_code, **kwargs):
        print("fifth--处理错误")

    def on_finish(self):
        print("sixth--处理结束,释放资源--")

9 模板语法

Tornado 大部分模板语法和 Django 类似。模板传参可以通过k=v的方式传递,也可以通过**dict的方式进行解包传递

class APIHandler(tornado.web.RequestHandler):
    def get(self):
        context = {
            "name": "Soloman",
            "age": 18,
            "hobby": ["篮球", "唱歌", "跳舞"]
        }
        self.render("api.html", **context)
        # self.render("api.html", name="Soloman", age=18, hobby=["篮球", "唱歌", "跳舞"])

模板中有两种表现形式,一种是以双层{}进行包裹,另一种是命令形式以% 命令 %包裹。注意:tornado的模板语法的结束标识是% end %,不是Django或jinja2的% endblock %

tornado中的模板语言允许导入Python包、模块

% import time %
{ time.time() }

% from util.modify import mul %
{mul(6,6)}

9.1 ui_modules

ui_modules定义一些公用的组件,在这里返回的字符串将默认关闭HTML字符转义功能。

需要覆写的方法描述
render()覆写该方法,返回该UI模块的输出
embedded_javascript()覆写该方法,返回一段JavaScript代码字符串,它将会在模板中自动添加script标签,并且该script标签内部会填入该方法返回的JavaScript代码字符串
javascript_files()覆写该方法,返回值应当是str,它将会在模板中自动添加script标签并且该script标签的src属性会指向该方法所返回的字符串,如果返回值是一个相对路径,则会去application的settings中寻找静态资源的path做拼接
embedded_css()覆写该方法,返回一段CSS代码字符串,它将会在模板中自动添加style标签,并且该style标签内部会填入该方法返回的CSS代码字符串
css_files()覆写该方法,返回值应当是str,它将会在模板中自动添加link标签并且该link标签的href属性会指向该方法所返回的字符串,如果返回值是一个相对路径,则会去application的settings中寻找静态资源的path做拼接
html_head()重写该方法,返回值将放置在head元素中的HTML字符串。
html_body()重写该方法,返回值将放置在body元素末尾的HTML字符串。
render_string()渲染模板并将其作为字符串返回。

10 cookies

10.1 基本操作

方法描述
self.get_cookie()获取cookie键值对
self.set_cookie()设置cookie键值对,参数expires可设置过期时间(日期:datetime/time,默认30天),expires_day设置过期天数(优先级更高),示例:expirse = time.time() + 60 * 60 * 24 * 7
class APIHandler(tornado.web.RequestHandler):
    def get(self):
        if self.get_cookie("access"):
            self.write("你来访问过了")
        else:
            self.set_cookie("access","yes")
            self.write("七天内可再次访问")

10.2 加密 cookies

application的配置项中设定一个加密的盐cookie_secret,然后使用下面的两个方法进行加密cookies的操作

方法描述
self.get_secure_cookie()获取cookie键值对,并对其进行解密
self.set_secure_cookie()设置cookie键值对,将其与cookie_secret进行结合加密
class APIHandler(tornado.web.RequestHandler):
    def get(self):
        if self.get_secure_cookie("access"):
            self.write("你来访问过了")
        else:
            self.set_secure_cookie("access", "yes")
            self.write("七天内可再次访问")


settings = {
    "debug": True,
    "template_path": "templates",  # 指定模板目录
    "static_path": "static",  # 指定静态文件目录
    "cookie_secret": "0dsa0D9d0a%39433**das9))|ddsa",  # cookie加密的盐
}

application = tornado.web.Application([
    tornado.web.url(r'/api', APIHandler),
], **settings)

tornado源码流程图示