这篇我们将学习Tornado的web基础。
用tornado写个hello world
我们使用tornado编写一个简单的web页面。
1 | from tornado import web |
这段简洁的代码特别想Flask的。
tornado中为什么不能写同步的方法
这是因为tornado核心是一个单线程,当我们在一个请求中使用了同步代码的时候,会阻塞其他请求的。
1 | import time |
上面是两个url,我们在第一个请求中做了一个延时的io操作,现在我们看下直接访问第二个url的耗时。

当我们先访问第一个阻塞url的时候,再看下耗时:

tornado中的url配置
上面我们已经配置了url,对于包含变量的url我们可以使用下面的方式。
1 | class PeopleInfoHandler(web.RequestHandler): |

使用URLSpec进行一些配置,我们先看下源码:
1 | class URLSpec(Rule): |
我们可以为每个url配置一个名字,方便内部访问。
1 | urls = [ |
比如我们可以在访问一个url的时候直接进行跳转
1 | class MainHandler(web.RequestHandler): |
我们可以在url请求的时候传入一些初始化参数,再处理请求之前获得这些初始化参数。
1 | class PeopleIdHandler(web.RequestHandler): |
我们在配置路由的时候传入初始化数据people_db。

这种访问初始化的一个应用场景:针对不同的url访问不同的数据库。
小节知识点:
- 使用
reverse_url通过参数名获得内部url可以进行可变参数拼接,类比flask的url_for。 - 使用
redirect重定向 - 可以给
Handler传一个初始值
关于 define,options,parse_comand_line
这个小节我们学习下如何在启动python模块的时候通过tornado的option模块传递一些参数。
1 | from tornado.options import define, options, parse_command_line |
看下define的作用:
定义一些可以在命令行中传递的参数以及类型。
1 | define('port', default=8008, help="run on the given port", type=int) |
完成上面配置之后,需要调用parse_command_line:
1 | options.parse_command_line() |
将运行python模块的命令进行解析,获得define中定义的各个参数,以属性值的形式放到options中。
注意:
options是一个类,全局只有一个
直接通过属性值获得参数值
1 | if __name__ == "__main__": |
完成上面配置可以直接运行脚本,访问服务。
1 | python options_test.py --port=8002 --debug=True |
除了从命令行获取到参数值,还可以通过parse_config_file从文件中获得:
我们新建一个文件conf.fg,输入我们要传递的参数。
1 | port=8002 |
通过parse_config_file指定文件名:
1 | options.parse_config_file("conf.cfg") |
这样直接运行脚本即可访问服务。
RequestHandler常用方法
我们看下官方文档已经将常用方法进行了分类:常用方法分类

我们先看下入口方法:官方文档
第一个是initialize方法,是初始化Handler类的时候定义的方法,用于接受定义url传入的参数。
1 | class ProfileHandler(RequestHandler): |
通过这个初始化方法之后将一些数据保存起来,供其他方法使用。
第二个方法是prepare,是在所有真正请求处理方法调用之前调用的方法,initialize是在prepare之前调用的。我们可以在prepare方法中打印日志,打开文件等操作。
Decorate this method with
gen.coroutineor useasync defto make it asynchronous
第三个方法是on_finish,这个方法和prepare是相反的,是所有请求结束之后调用的。我们可以在方法中进行关闭句柄,清理缓存等操作。
上面两个方法可以理解为Flask的请求钩子。
还有就是一些 HTTP方法:
1 | def get(self, *args, **kwargs): |
看下输入方法有哪些:官方文档
输入方法主要处理传入的一些参数。
先看下get_query_argument和get_query_arguments两个方法。
get_query_argument:
从请求的query string返回给定name的参数的值.
如果没有提供默认值, 这个参数将被视为必须的, 并且当找不到这个 参数的时候我们会抛出一个 MissingArgumentError 异常.
如果这个参数在url中多次出现, 我们将返回最后一次的值.
返回值永远是unicode.
get_query_arguments:
返回指定name的参数列表.
如果参数不存在, 将返回空列表.
返回值永远是unicode.
这两个方法获得的都是通过
url传参。
接着我们看下get_argument和get_arguments两个方法。
这两个方法和上面两个类似,不过除了能够获得通过url传递的参数外,还能获得post方式的传参。
get_argument:
返回指定的name参数的值.
如果没有提供默认值, 那么这个参数将被视为是必须的, 并且当 找不到这个参数的时候我们会抛出一个 MissingArgumentError.
如果一个参数在url上出现多次, 我们返回最后一个值.
返回值永远是unicode.
get_arguments:
返回指定name的参数列表.
如果url传参外还通过post方式传参,都会将参数放到列表。
如果参数不存在, 返回一个空列表.
返回值永远是unicode.
我们可以直接获取到所有的参数通过
self.request.arguments
想要获得参数我们还可以使用get_body_argument和get_body_arguments:
下面我们发送一个带有json数据的post请求:
1 | requests.post("http://127.0.0.1:8888/?name=tornado", json={ |

我们通过url传递的参数是在arguments,query中,body存的是byte类型而body_arguments为空。
这时候直接通过get_body_argument会获得:
我们看下get_body_argument的源码:
1 | def get_body_argument(self, name, default=_ARG_DEFAULT, strip=True): |
从源码看到是从request.body_arguments获得参数。
但是我们从上面图中看到在传递json数据的时候并没有将数据放到body_arguments中,而是放到body中。
我们现在从request.body中获得数据。

那我们怎么使用get_body_argument方法获得数据呢?
我们需要指定传输数据的form类型。
1 | import requests |

输出方法:官方文档
使用set_status方法设置返回的状态码:
- status_code (int) – 响应状态码. 如果
reason是None, 它必须存在于httplib.responses. - reason (string) – 用人类可读的原因短语来描述状态码. 如果是
None, 它会由来自httplib.responses的reason填满.
使用write返回数据:
我们可以调用多个write:
1 | self.write("hello") |
返回的数据将是helloworld。这是因为RequestHandler是一个长连接如果不关闭会一直写。每调用一次将数据写入到缓冲区,最后将数据统一发送出去。
使用finish方法结束HTTP请求,结束响应:
我们在调用finish方法之后就不能再使用write了。因为连接已经被关闭了。
我们可以在finish中传入一个字典 ,将会自动返回一个json数据。
1 | self.finish({ |
我们使用redirect进行重定向:
重定向到给定的URL(可以选择相对路径).
如果指定了 status 参数, 这个值将作为HTTP状态码; 否则 将通过 permanent 参数选择301 (永久) 或者 302 (临时). 默认是 302 (临时重定向).
我们使用write_error进行错误页面的重写:
write_error 可能调用 write, render, set_header,等 来产生一般的输出.