Tornaod框架
Tornado 是 FriendFeed 使用的可扩展的非阻塞式 web 服务器及其相关工具的开源版本。这个 Web 框架看起来有些像web.py 或者 Google 的 webapp,不过为了能有效利用非阻塞式服务器环境,这个 Web 框架还包含了一些相关的有用工具 和优化。
Tornado 和现在的主流 Web 服务器框架(包括大多数 Python 的框架)有着明显的区别:它是非阻塞式服务器,而且速度相当快。得利于其 非阻塞的方式和对 epoll 的运用,Tornado 每秒可以处理数以千计的连接,这意味着对于实时 Web 服务来说,Tornado 是一个理想的 Web 框架。我们开发这个 Web 服务器的主要目的就是为了处理 FriendFeed 的实时功能 ——在 FriendFeed 的应用里每一个活动用户都会保持着一个服务器连接。(关于如何扩容 服务器,以处理数以千计的客户端的连接的问题,请参阅 C10K problem。)
一、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),#(r"/api/make_doc/(?P<aid>\w+)_(?P<bid>\w+)$", aa),
])
if __name__ == "__main__":
application.listen(8888)
#application.listen(port=8888, address="0.0.0.0") 加入4个0的意思就是开启默认路由,运行别人访问我的这个程序,通常用于本地测试
tornado.ioloop.IOLoop.instance().start()
基本所有的Web框架都是一下的流程:
准备工作:
加载配置文件
加载路由映射 application = tornado.web.Application([(r"/index", MainHandler),])
创建socket
循环阶段:
类似socket Server不断的循环文件句柄,当有请求过来的时候,根据用户的请求方法来判断是什么请求,在通过反射来执行相应的函数或类。
运行流程:
第一步:执行脚本,监听 8888 端口 第二步:浏览器客户端访问 /index --> http://127.0.0.1:8888/index 第三步:服务器接受请求,并交由对应的类处理该请求 第四步:类接受到请求之后,根据请求方式(post / get / delete ...)的不同调用并执行相应的方法 第五步:方法返回值的字符串内容发送浏览器
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#!/usr/bin/env python # -*- coding:utf-8 -*- import tornado.ioloop import tornado.web from tornado import httpclient from tornado.web import asynchronous from tornado import gen import uimodules as md import uimethods as mt class MainHandler(tornado.web.RequestHandler): @asynchronous @gen.coroutine def get(self): print 'start get ' http = httpclient.AsyncHTTPClient() http.fetch("http://127.0.0.1:8008/post/", self.callback) self.write('end') def callback(self, response): print response.body settings = { 'template_path': 'template', 'static_path': 'static', 'static_url_prefix': '/static/', 'ui_methods': mt, 'ui_modules': md, } application = tornado.web.Application([ (r"/index", MainHandler), ], **settings) 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()
Tornado中原生支持二级域名的路由,如:
----------------------------------------------------------
方法二:
方法三:
用self.get_arguments更好一点,不会报错, 如果值为空,会得到空列表
模板
Tornao中的模板语言和django中类似,模板引擎将模板文件载入内存,然后将数据嵌入其中,最终获取到一个完整的字符串,再将字符串返回给请求者。
Tornado 的模板支持“控制语句”和“表达语句”,控制语句是使用 {%
和 %}
包起来的 例如 {% if len(items) > 2 %}
。表达语句是使用 {{
和 }}
包起来的,例如 {{ items[0] }}
。
控制语句和对应的 Python 语句的格式基本完全相同。我们支持 if
、for
、while
和 try
,这些语句逻辑结束的位置需要用 {% end %}
做标记。还通过 extends
和 block
语句实现了模板继承。这些在 template
模块 的代码文档中有着详细的描述。
基本使用:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#!/usr/bin/env python # -*- coding:utf-8 -*- import tornado.ioloop import tornado.web class MainHandler(tornado.web.RequestHandler): def get(self): self.render("index.html", list_info = [11,22,33]) application = tornado.web.Application([ (r"/index", MainHandler), ]) if __name__ == "__main__": application.listen(8888) tornado.ioloop.IOLoop.instance().start()
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <title>老男孩</title> <link href="{{static_url("css/common.css")}}" rel="stylesheet" /> </head> <body> <div> <ul> {% for item in list_info %} <li>{{item}}</li> {% end %} </ul> </div> <script src="{{static_url("js/jquery-1.8.2.min.js")}}"></script> </body> </html>
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
在模板中默认提供了一些函数、字段、类以供模板使用: 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 的別名
目录结构:
如果是前后端分离,访问图片路径原则:
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title></title> <!--<script src="{{static_url('jquery.min.js')}}"></script>--> <script src="../static/jquery.min.js"></script> </head> <body> <div> <!--<img src="{{static_url('test.jpg')}}">--> <img src="http://192.168.1.137:8888/static/test.jpg"> </div> </body> </html>
模板继承
多个子模板可以继承一个主模板, 那逆向思维,是不是一个主页面也可以有多个子模板类似轮播图, 然后进行判断呢, 用include
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>shuaige</title> {% block css%} <!--这里对应子版的css路径--> {% end %} </head> <body> <div><h1>TEST</h1></div> {% block htmlbody %}{% end %} <script src="{{static_url('js/jquery-1.8.2.min.js')}}"></script> {% block JavaScript %}{% end %} </body> </html>
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
{% extends 'layout.html' %} {% block css %} <link href="{{static_url('css/index.css')}}" rel="stylesheet" /> {% end %} {% block htmlbody %} <h1 id="shuaige" class="tim">没有取值,就先这样吧,循环就先不写了.</h1> {% end %} {% block JavaScript %} {% end %}
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#!/usr/bin/env python # -*- coding:utf-8 -*- import tornado.ioloop import tornado.web class MainHandler(tornado.web.RequestHandler): def get(self): self.render('index.html') settings = { 'template_path': 'template', 'static_path': 'static', 'static_url_prefix': '/static/', } application = tornado.web.Application([ (r"/index", MainHandler), ], **settings) if __name__ == "__main__": application.listen(8888) tornado.ioloop.IOLoop.instance().start()
模板 --> 一个HTML内部有多个子模板可进行判断使用--》include
导入 include
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<div>
<ul>
<li>1024</li>
<li>42区</li>
</ul>
</div>
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <title>老男孩</title> <link href="{{static_url("css/common.css")}}" rel="stylesheet" /> </head> <body> <div class="pg-header"> {% include 'header.html' %} </div> <script src="{{static_url("js/jquery-1.8.2.min.js")}}"></script> </body> </html>
在模板中默认提供了一些函数、字段、类以供模板使用:
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
: forhandler.static_url
的別名xsrf_form_html
:handler.xsrf_form_html
的別名
2、自定义模板
Tornado默认提供的这些功能其实本质上就是 UIMethod 和 UIModule,我们也可以自定义从而实现类似于Django的simple_tag的功能:
1、定义
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#!/usr/bin/env python # -*- coding:utf-8 -*- def tab(self): return 'UIMethod' #def func(self,arg): # return arg.lower()
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#!/usr/bin/env python # -*- coding:utf-8 -*- from tornado.web import UIModule from tornado import escape class custom(UIModule): def render(self, *args, **kwargs): return escape.xhtml_escape('<h1>德玛西亚</h1>') #return escape.xhtml_escape('<h1>www</h1>')
2.注册
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#!/usr/bin/env python # -*- coding:utf-8 -*- #!/usr/bin/env python # -*- coding:utf-8 -*- import tornado.ioloop import tornado.web from tornado.escape import linkify import uimodules as md import uimethods as mt class MainHandler(tornado.web.RequestHandler): def get(self): self.render('index.html') settings = { 'template_path': 'template', 'static_path': 'static', 'static_url_prefix': '/static/', 'ui_methods': mt, 'ui_modules': md, } application = tornado.web.Application([ (r"/index", MainHandler), ], **settings) if __name__ == "__main__": application.listen(8009) tornado.ioloop.IOLoop.instance().start()
3.使用
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title></title> <link href="{{static_url("commons.css")}}" rel="stylesheet" /> </head> <body> <h1>hello</h1> {% module custom(123) %} {{ tab() }} </body>
实用功能
1.静态文件
#!/usr/bin/env python # -*- coding:utf-8 -*- import tornado.ioloop import tornado.web class MainHandler(tornado.web.RequestHandler): def get(self): self.render('index.html') settings = { 'template_path': 'template', 'static_path': 'static', 'static_url_prefix': '/static/', } application = tornado.web.Application([ (r"/index", MainHandler), ], **settings) if __name__ == "__main__": application.listen(8888) tornado.ioloop.IOLoop.instance().start()
在html中引用的时候
<link href="{{static_url('css/index.css')}}" rel="stylesheet" />
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title></title> <link href="{{static_url("commons.css")}}" rel="stylesheet" /> </head> <body> <h1>hello</h1> </body> </html>
目录结构:
静态文件缓存:
<link href="{{static_url('css/index.css')}}" rel="stylesheet" /> #这里static_url 为什么不写路径呢? ''' 在Django中,我们写成变量形式的主要是为了以后扩展方便。但是在Tornado中不仅有这个功能还有一个缓存的功能 '''
原理:
拿一个静态文件来说:/static/commons.js如果用Tornado封装后,类似于给他加了一个版本号/static/commons.js?v=sldkfjsldf123
当客户端访问过来的时候会携带这个值,如果发现没变客户端缓存的静态文件直接渲染就行了,不必再在服务器上下载一下静态文件了。
Tornado静态文件实现缓存源代码:
def get_content_version(cls, abspath): """Returns a version string for the resource at the given path. This class method may be overridden by subclasses. The default implementation is a hash of the file's contents. .. versionadded:: 3.1 """ data = cls.get_content(abspath) hasher = hashlib.md5() if isinstance(data, bytes): hasher.update(data) else: for chunk in data: hasher.update(chunk) return hasher.hexdigest()
2.CSRF跨站伪造请求
Tornado中的夸张请求伪造和Django中的相似,跨站伪造请求(Cross-site request forgery)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
settings = { "xsrf_cookies": True, } application = tornado.web.Application([ (r"/", MainHandler), (r"/login", LoginHandler), ], **settings)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<form action="/new_message" method="post"> {{ xsrf_form_html() }} <input type="text" name="message"/> <input type="submit" value="Post"/> </form>
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 + ")")); }}); };
注:Ajax使用时,本质上就是去获取本地的cookie,携带cookie再来发送请求
出处:http://www.cnblogs.com/renfanzi/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接。