目录
Tornado的特点
Tornado(龙卷风)和Django一样是Python中比较主流的web框架,Tornado 和现在的主流 Web 服务器框架也有着明显的区别:Tornado自带socket,并且实现了异步非阻塞,而且对WebSocket协议天然支持。
相对于其他Python网络框架,它有如下特点:
- 完整的web框架:与Django、Flask等一样,Tornado也提供URL路由映射、Request上下文、基于模板的页面渲染技术等开发Web应用的必备工具。
- 是一个高效的网络库,性能与Twisted、Gevent等底层Python框架想媲美:提供了异步I/O支持、超时事件处理。这使得Tornado除了可以作为Web应用服务器框架,还可以用来做爬虫、游戏服务器等后台应用。
- 提供高效的内部HTTP服务器:虽然其他框架也提供了内部HTTP服务器,但它们的HTTP服务器由于性能原因只能用于测试环境。而Tornado的HTTP服务器与Tornado异步调用紧密结合,可以之间用于生产环境。
- 完备的websocket支持:WebSocket是HTML5的一种新标准,实现了浏览器与服务器之间的双向实时通信。
由于Tornado的上述特点,Tornado常被用作大型站点的接口服务框架,而不像Django那样着眼于简历完整的大型网站。
Tornado的基本组成
Tonado由 路由系统、视图、模板语言等组成,如果习惯了使用Django你会感觉它功能单薄,但是只有这样才能足够轻量,如果用到什么功能就自己去GitHub上找现成的插件,或者自实现;以下将对这些基本组件进行逐一介绍。
Django功能概览:
socket:有
中间件:无(使用Python的wsgiref模块)
路由系统:有
视图函数:有
ORM操作:有
模板语言:有
simple_tag:有
cokies:有
session:有
csrf:有
xss:有
其他:缓存、信号、Form组件、ModelFormm、Admin
tornado功能概览:
socket:有(异步非阻塞、支持WebScoket)
路由系统:有
视图函数:有
静态文件:有
ORM操作:无
模板语言:有
simple_tag:有,uimethod,uimodule
cokies:有
session:无
csrf:有
xss:有
其他:无
Django | Tornado | |
---|---|---|
socket | 有 | 有 |
路由系统 | 有 | 有 |
视图函数 | 有 | 有 |
静态文件 | 有 | 有 |
模板语言 | 有 | 有 |
ORM操作 | 有 | 无 |
simple_tag | 有 | 有uimethod,uimodule |
cokies | 有 | 有 |
session | 有 | 无 |
csrf | 有 | 有 |
xss | 有 | 有 |
其他 | 缓存、信号、Form组件、ModelFormm、Admin | 无 |
Tornado的基本功能
安装:
pip install tornado
基本流程
import tornado.ioloop
import tornado.web
# 注意继承RequestHandler 而不是redirectHandler
class MainHandler(tornado.web.RequestHandler):
def get(self):
"""get请求"""
self.write("Hello World!")
def make_app():
return tornado.web.Application(
[(r"/index", MainHandler), ] # 路由映射到类
)
def main():
app = make_app() # URL路由映射
app.listen(8888) # 创建1个socket对象,监听8888端口
tornado.ioloop.IOLoop.instance().start()
# 启动IOLoop,该函数一直运行且不退出,用于处理完所有客户端的访问请求
# 相当于conn, addr=socket.accept()进入监听状态
第一步:执行脚本,监听 8888 端口
第二步:浏览器客户端访问 /index --> http://127.0.0.1:8888/index/
第三步:服务器接受请求,根据URL的访问映射到RequestHandler的子类,并交由对应的类MainHandler处理该请求
第四步:MainHandler类接受到请求之后,根据请求方式(post / get / delete ...)的不同调用并执行相应的方法
第五步:方法返回值的字符串内容发送浏览器
配置
setings={
'template_path':'templates', # 配置模板路径
'static_path':'static', # 配置静态文件存放的路径
'static_url_prefix':'/static/', # 在模板中引用静态文件路径时使用的别名 注意是模板引用时的别名,必须以/开头以/结尾
"xsrf_cookies": True, # 使用xsrf认证
'cookie_secret' :'xsseffekrjewkhwy' # cokies加密时使用的盐
}
# 加载路由和配置
application=tornado.web.Application(
[(r'/login/',LoginHandler) ,
(r'/index/',IndexHandler) ,
], # 参数1 路由系统
**setings # 参数2 配置文件
)
路由系统
与Django类似,用正则字符串进行路由匹配。有两种:固定字符串路径和参数字符串路径。
动态路由:
app=tornado.web.Application(
[
(r'^/index/$',MainHandler),
(r'^/index/(\d+)$',MainHandler), #url传参
]
)
域名匹配:
#支持域名匹配 www.xxx.com:8888/index/333333
app.add_handlers('www.xxx.com',[
(r'^/index/$', MainHandler),
(r'^/index/(\d+)$', MainHandler),
])
反向生成url:
app.add_handlers('www.zhanggen.com',[
(r'^/index/$', MainHandler,{},"name1"), #反向生成url
(r'^/index/(\d+)$', MainHandler,{},"name2"),
])
class MainHandler(tornado.web.RequestHandler):
def get(self,*args,**kwargs):
url1=self.application.reverse_url('name1')
# reverse_url 反向解析
url2 = self.application.reverse_url('name2', 666)
print(url1,url2)
self.write('hello word')
视图 RequestHandler
tornado的视图才有CBV模式,url匹配成功之后先 视图执行顺序为 initialize 、prepare、get/post/put/delete、finish;
接入点函数:需要子类继承并定义具体行为的函数在RequestHandler中被称为接入点函数(Entry Point),例如前面的get()函数就是接入点函数。
RequestHandler.initialize():
该方法被子类重写,实现RequestHandler子类实例的初始化过程。可以为该函数传递参数,参数来源于配置URL映射时的定义。
class ProfileHandler(tornado.web.RequestHandler):
def initialize(self, database):
self.database = database
def get(self):
pass
app = tornado.web.Application([(r'/account',ProfileHandler, dict(database="c:\\example.db"))])
# initialize有参数database,在定义URL映射时以dict方式给出
RequestHandler.prepare():
prepare()方法用于调用请求处理(get,post等)方法之前的初始化处理,可以根据实际需求进行重写。通常可以用prepare()方法做资源初始化操作。
RequestHandler.finish():
finish()方法用于请求处理结束后的一些清理工作,例如可以做清理对象占用的内存或者关闭数据库连接等。
RequestHandler.get()/post():
每个http action在RequestHandler中都已单独的函数进行处理,都是以对应的HTTP方法小写的方式名命。
- RequestHandler.get(self,*args,**kwargs)
- RequestHandler.head(self,*args,**kwargs)
- RequestHandler.post(self,*args,**kwargs)
- RequestHandler.delete(self,*args,**kwargs)
- RequestHandler.patch(self,*args,**kwargs)
- RequestHandler.put(self,*args,**kwargs)
- RequestHandler.options(self,*args,**kwargs)
import tornado.ioloop
import tornado.web
class MainHandler(tornado.web.RequestHandler):
def initialize(self): #1
print()
def prepare(self):
pass
def get(self,*args,**kwargs):
self.write('hello word')
def post(self, *args, **kwargs):
pass
def finish(self, chunk=None):
pass
super(self,MainHandler).finish()
请求相关输入捕获
输入捕获是指在RequestHandler中用于获取客户端输入的工具函数和属性,比如获取url中的参数、post提交的参数等。
-
self.get_argument('user'): 获取GET和POST请求携带的参数
-
self.get_arguments('user_list'):获取GET和POST请求参数列表(如chebox标签和select多选)
-
self.get_query_argument('user'):获取GET请求携带的参数
-
self.get_query_arguments('user_list'):获取GET请求参数列表(如chebox标签和select多选)
-
self.get_body_argument('user'):获取POST请求携带的参数
-
self.get_body_arguments('user_list'):获取POST请求参数列表(如chebox标签和select多选)
-
self.request.body.decode('utf-8'):获取json数据
-
self.get_cookie(name, default=None) : 根据cookie名称获取cookie值
-
self.request :返回itornado.httputil.HTTPServerRequest对象实例的苏醒,通过该对象可以获取关于HTTP请求的一切信息,
import tornado.web class DetailHandler(tornado.web.RequestHandler): def get(self): remote_ip = self.request.remote_ip # 获取客户端的IP地址 host = self.request.host # 获取请求的主机地址
技巧:在一般情况下 用self.get_argument('user',None)
/self.get_arguments('user',None)
即可,因为它们不管是GET还是POST,都可以用它们来获取请求携带的参数,相当于是一种合集。
注:以上取值方式如果取不到值就会报错,可以设置取不到值就取None;(例如 self.get_argument('user',None))
输出响应
输出响应函数是指一组为客户端生成处理结果的工具函数,扩展url的处理结果。下面列举常用的三种:
-
self.write(chunk)
: 一般情况下,用于输出字符串到客户端。如果是一个字典,则会以JSON格式发送给客户端,同时将请求头中的Content_type设置成 application/json。 -
self.render(template_name, **kwargs)
: 用于响应页面,用给定的参数渲染模板,可以在本函数中传入模板文件名称和模板的参数。 -
self.redirect(url,)
: 重定向页面。
模板语言
tornado的模板语言和python语法一致。
登录示例:
import tornado.ioloop
import tornado.web
class LoginHandler(tornado.web.RequestHandler):
def get(self):
self.render('login.html')
settings={
'template_path':'templates',#配置模板路径
'static_path':'static', #配置静态文件存放的路径
'static_url_prefix':'/static/' #在模板中引用静态文件路径时使用的别名 注意是模板引用时的别名
}
app=tornado.web.Application([
(r'/login/',LoginHandler) #参数1 路由系统
],
**settings #参数2 配置文件
)
if __name__ == '__main__':
app.listen(8888) #创建1个socket对象
tornado.ioloop.IOLoop.instance().start()
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="/static/dist/css/bootstrap.css">
<title>Title</title>
</head>
<body>
<div class="container">
<div class="row">
<div class="col-md-5 col-md-offset-3">
<form method="post" >
<div class="form-group">
<label for="exampleInputEmail1">用户名</label>
<input type="email" class="form-control" id="exampleInputEmail1" placeholder="用户名">
</div>
<div class="form-group">
<label for="exampleInputPassword1">密码</label>
<input type="password" class="form-control" id="exampleInputPassword1" placeholder="密码">
</div>
<button type="submit" class="btn btn-default">提交</button>
</form>
</div>
</div>
</div>
</body>
</html>
引入静态文件
通过别名引入静态文件:
<link rel="stylesheet" href="/static/coment.css">
static_url()方式一个如静态文件:
<link rel="stylesheet" href='{{static_url("dist/css/bootstrap.css") }}'>
通过static_url()方法引入静态文件的好处:
1、使用static_url()可以不用考虑静态文件修改之后造成引用失效的情况;
2、还会生成静态文件url会有一个v=...的参数,这是tornado根据静态文件MD5之后的值,如果后台的静态文件修改,这个值就会变化,前端就会重新向后台请求静态文件,保证页面实时更新,不引用浏览器缓存;
上下文
如果模板语言中声明了变量,上下文对象必须对应传值,如果没有就设置为空,否则会报错;
self.render('login.html',**{'erro_msg':'' }) #模板中声明了变量,视图必须传值,如果没有就设置为空;