tornado的使用小技巧

毕业设计的内容涉及到了tornado,现在来总结一些使用中用到的小技巧。

集成常用操作到BaseHandler

在Tornado中有时候会有很多全局的任务,比如连接查询数据库。为了方便可以把数据库查询的任务写成BaseHandler的一个方法,比如下面:

1
2
3
4
5
6
7
8
9
10
11
12
class Application(tornado.web.Application):
def __init__(self):
self.db = pymongo.MongoClient('localhost',27017)['test']
...
class BaseHandler(tornado.web.RequestHandler):
def db_find_one(self, coll, param):
return self.application.db[coll].find_one(param)
class IndexHandler(BaseHandler):
def get():
user = self.db_find_one('user',{'name':'Bob'})

定制使用xsrf安全控制

如果开启xsrf_cookies后,所有的POST方法的表单都需要提交一个_xsrf字段的值。不过在Tornado已经内置自动添加这个字段的操作,因此既方便又安全。使用方法是在页面的form中添加两行内容:

1
2
3
4
5
6
<form action="/login" method="post">
{% autoescape None %}
{{ xsrf_form_html() }}
<input type="text" name="username" >
</form>

这样在数据提交到后台时,Tornado会自动获取_xsrf字段的值并自动进行比对验证。

这样不需要开发者手动验证很方便,但是如果有这样一个情况,我们要使用一个别人提供的采用POST的服务,无法自动添加这个_xsrf时怎么办呢?
其实方法也很简单,我们只需要在相应的服务处理类中复写一下check_xsrf_cookie函数就可以了如果写pass就是不处理。示例如下:

1
2
3
4
5
6
class OriginServiceHandler(BaseHandler):
def check_xsrf_cookie(self):
pass
def post(self):
# do something here...

参见:check_xsrf_cookie使用说明

Tornado对WebSocket的跨域验证

Tornado对WebSocket的支持非常好用,但是会对WebSocket的连接进行跨域检查,如果不加处理都会返回错误信息:

1
tornado 403 GET warning when opening websocket

对于Tornado 4.0可以采用复写check_origin函数的方法:

1
2
3
4
5
6
class WebSocketHandler(tornado.websocket.WebSocketHandler):
def check_origin(self, origin):
return True
def open(self):
self.write_message('hello')

或者限定域名

1
2
3
def check_origin(self, origin):
parsed_origin = urllib.parse.urlparse(origin)
return parsed_origin.netloc.endswith(".mydomain.com")

对于tornado 3.0只能重写Header的方法来处理:

1
2
3
4
5
6
7
8
9
class WebSocketHandler(tornado.websocket.WebSocketHandler):
def set_default_headers(self):
self.set_header('Access-Control-Allow-Origin', '*')
self.set_header('Access-Control-Allow-Methods', 'POST, GET, OPTIONS')
self.set_header('Access-Control-Max-Age', 1000)
self.set_header('Access-Control-Allow-Headers', '*')
def open(self):
self.write_message('hello')

参见:websocket 跨域问题

Tornado异步执行定时任务

之前做微信开发的时候,需要每7000秒刷新下access_token,本来想用Python的多进程来处理,但是在Windows上的多进程太费事了,因此使用了Tornado内置的一个方法PeriodicCallback用于处理周期调用。我的使用方法如下:

1
2
3
4
5
6
7
8
9
class MyHandler(tornado.web.request):
def __init__(self):
self.fun()
obj = tornado.ioloop.PeriodicCallback(self.fun, 7000*1000)
obj.start()
# obj.start() 是停止循环执行
def fun(self):
# do something here ...

使用的时候只用实例化这个类既可以了。由于PeriodicCallback是周期调用,因此在刚开始的时候需要手动调用一次。之后就会每7000秒执行一次。这个用起来就是time.sleep()的不阻塞版本。