千家信息网

Python中如何使用Tornado

发表于:2024-11-11 作者:千家信息网编辑
千家信息网最后更新 2024年11月11日,这篇文章给大家分享的是有关Python中如何使用Tornado的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。Tornado 基本操作Hello World经典的 hello
千家信息网最后更新 2024年11月11日Python中如何使用Tornado

这篇文章给大家分享的是有关Python中如何使用Tornado的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。

Tornado 基本操作

Hello World

经典的 hello world 示例:

import tornado.web# 视图class MainHandler(tornado.web.RequestHandler):    def get(self):        self.write("Hello World.")# 路由application = tornado.web.Application([    (r"/", MainHandler),    (r"/hello", MainHandler),])if __name__ == '__main__':    import tornado.ioloop    application.listen(8000)    tornado.ioloop.IOLoop.instance().start()

整个过程其实就是在创建一个socket服务端并监听8000端口。当请求到来时,根据请求中的url和请求方式(post、get或put等)来指定相应的类中的方法来处理本次请求。在上述示例中 url 在路由系统匹配到时,则服务器会给浏览器返回 Hello World ,否则返回 404: Not Found(tornado内部定义的值), 即完成一次http请求和响应。

模板引擎

Tornao中的模板语言和django中类似。模板引擎将模板文件载入内存,然后将数据嵌入其中,最终获取到一个完整的字符串,再将字符串返回给请求者。
不过还是有区别的。Tornado 的模板支持"控制语句"和"表达语句",控制语句是使用 {% 和 %} 包起来的。例如 {% if len(items) > 2 %}。表达语句是使用 {{ 和 }} 包起来的,例如 {{ items[0] }} 。
控制语句和对应的 Python 语句的格式基本完全相同。支持 if、for、while 和 try,这些语句逻辑结束的位置需要用 {% end %} 做标记。还通过 extends 和 block 语句实现了模板继承。这些在 template 模块的代码文档中有着详细的描述。在使用模板前需要在setting中设置模板路径:"template_path" : "tpl"
使用模板引擎的简单示例,后端代码:

import tornado.webclass IndexHandler(tornado.web.RequestHandler):    def get(self):        self.render("index.html", k1='v1', k2='v2')  # k1和k2是传给模板引擎处理的内容application = tornado.web.Application([    (r"/index", IndexHandler),])if __name__ == '__main__':    import tornado.ioloop    application.listen(8000)    tornado.ioloop.IOLoop.instance().start()

前端代码,模板语言的使用:

Hello World

{{ k1 }}

{% if k2 == 'v2' %}

k2 == v2

{% else %}}

k2 != v2

{% end %}

加载配置

上面的前端代码,最好是统一保存在某个目录里,比如新建个tpl目录来存放。把html文件移过去之后,现在render()方法就找不到这个文件了。当然可以改一下参数,把目录名加进去。不过推荐的做法是把tpl目录加到配置里去,对上面的代码进去修改,加入配置信息:

class IndexHandler(tornado.web.RequestHandler):    def get(self):        self.render("index.html", k1='v1', k2='v2')# 配置就是个key-value的字段settings = {    'template_path': 'tpl'}application = tornado.web.Application([    (r"/index", IndexHandler),], **settings)  # application加载配置信息

POST

先准备好如下的页面,在输入框里填入要搜索的关键字,提交后就跳转到搜索引擎搜索的结果:

后端的代码:

import tornado.webclass SearchHandler(tornado.web.RequestHandler):    def get(self):        self.render("baidu.html")    def post(self):        wd = self.get_argument('wd')        print(wd)        self.redirect('https://www.baidu.com/s?wd=%s' % wd)settings = {    'template_path': 'tpl'}application = tornado.web.Application([    (r"/baidu", SearchHandler),], **settings)if __name__ == '__main__':    import tornado.ioloop    application.listen(8000)    tornado.ioloop.IOLoop.instance().start()

上面的示例,post请求最后是用redirect()返回的,这个是页面的跳转。
获取提交的参数的方法有这些:

class LoginHandler(tornado.web.RequestHandler):    def post(self):        # 获取URL中以GET形式传递的数据        self.get_query_argument()        self.get_query_arguments()        # 获取请求体中以POST形式传递的数据        self.get_body_argument()        self.get_body_arguments()        # 从上面2个里都尝试获取        self.get_argument()        self.get_arguments()

静态文件(图片)

静态文件是给用户直接下载的,所以应该单独存放,并且在配置里注册对应的目录。配置的写法:

settings = {    'template_path': 'tpl',  # 模板    'static_path': 'imgs',  # 静态文件}

现在可以根据配置里的名称去创建一个新的文件夹 static 用来存放静态文件。然后放张图片进去。
这里故意不用static作为静态文件文件夹的名称,这里只是注册文件夹,但是前端引用的时候,无论你的静态文件放在那里,都是用 static/文件名称 。
加一个img标签到html里,然后验证一下效果。注意src里用的是static,而不是文件夹真正的名称:

这里前端引用的是必须用static,不过这个名字也是可以自定义的:

settings = {    'template_path': 'tpl',  # 模板    'static_path': 'imgs',  # 静态文件    'static_url_prefix': '/statics/',  # 注意两边都要有斜杠/}

其他操作

self.request.cookies : 获取cookies
self.set_cookie() : 设置cookie
self.request.headers : 获取请求头
self.set_header() : 设置响应头,如果出现同一个响应头,会覆盖
self.add_header() : 设置响应头,如果出现同一个响应头,则追加

Tornado 没有提供 session ,所以要用的话,得另外写。同样的,缓存也没有。

进阶操作

最基本的就是上面那些了,这里再补充一点别的。

自定义UIMethod以及UIModule

这个就是模板引擎里的自定义函数。
UIMethod 自定义的是个函数,UIModule 自定义的是个类。

定义
把自定义的函数和自定义的类单独写在文件里:

# ui_methods.pydef test1(self):  # 这里的self不能去掉    return "TEST1"def test2(self):    return "TEST2"# ui_module.pyfrom tornado.web import UIModulefrom tornado import escapeclass Test(UIModule):    def render(self, *args, **kwargs):        return escape.xhtml_escape('

UI Module TEST

')

注册
写一个完整的服务,这里加上注册的代码。先导入上面的文件,然后分别在settings里注册:

import tornado.webimport ui_methods as mtimport ui_modules as mdclass MainHandler(tornado.web.RequestHandler):    def get(self):        self.render('ui.html')settings = {    'template_path': 'tpl',  # 模板    'static_path': 'static',  # 静态文件,这里不重要    'static_url_prefix': '/statics/',  # 注意两边都要有斜杠/    'ui_methods': mt,    'ui_modules': md,}application = tornado.web.Application([    (r"/ui", MainHandler),], **settings)if __name__ == '__main__':    import tornado.ioloop    application.listen(8000)    tornado.ioloop.IOLoop.instance().start()

使用
这里只需要看明白前端调用的方法就可以了

UI Method

{{ test1() }}

{{ test2() }}

UI Module

{% module Test() %}

UIModule里的方法
render 方法返回的内容就是调用模板的位置显示的内容:

class Test(UIModule):    def javascript_files(self):        pass    def embedded_javascript(self):        pass    def css_files(self):        pass    def embedded_css(self):        pass    def render(self, *args, **kwargs):        return escape.xhtml_escape('

UI Module TEST

')

javascript的方法会在body的尾部插入script标签,插入js代码
css的方法则会在head里插入style标签,设置css
files就是直接引入文件,进行设置
embedded就是插入返回的字符串作为设置

CSRF

首先在settings里启用csrf:

settings = {    "xsrf_cookies": True,}

在 form 中使用

{% raw xsrf_form_html() %}

{{ xsrf_form_html() }} 能够输出完整的input标签,但是直接用回被解析为字符串,带着标签的信息作为字符串显示出来。所以上面用的是 {% raw xsrf_form_html() %} 。

在 Ajax 中使用
Ajax使用时,本质上就是去获取本地的cookie,携带cookie再来发送请求:

function getCookie(name) {    var r = [xss_clean].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 + ")"));    }});};

上传文件

先准备一个form表单上传文件的html页面:

        上传文件    

接收上传文件:

import tornado.webclass MainHandler(tornado.web.RequestHandler):    def get(self):        self.render('index.html')    def post(self, *args, **kwargs):        file_metas = self.request.files["fff"]        # print(file_metas)        for meta in file_metas:            file_name = meta['filename']            with open(file_name,'wb') as up:                up.write(meta['body'])settings = {    'template_path': 'template',}application = tornado.web.Application([    (r"/index", MainHandler),], **settings)if __name__ == '__main__':    import tornado.ioloop    application.listen(8000)    tornado.ioloop.IOLoop.instance().start()

上传文件还可以用Ajax,另外还有借助iframe标签实现的伪Ajax的实现,略...

异步非阻塞

异步非阻塞IO,高并发高性能是tornado的特点,所以这小节很重要。但是具体内容也没搞明白,只能尽量先记一些。
首先要引入下面的2个模块:

from tornado import genfrom tornado.concurrent import Futureclass AsyncHandler(tornado.web.RequestHandler):    @gen.coroutine    def get(self):        future = Future()        future.add_done_callback(self.doing)        yield future    def doing(self,*args, **kwargs):        self.write('async')        self.finish()

当发送GET请求时,由于方法被@gen.coroutine装饰且yield 一个Future对象,那么Tornado会等待,等待用户向future对象中放置数据或者发送信号,如果获取到数据或信号之后,就开始执行doing方法。
这里发送请求后,永远也不会返回,就是按上面说的Tornado一直在等待。等待调用了 future.set_result(result) 这个方法。之后就会调用回调函数,而set_result方法里传递进去的参数,可以通过 future.result() 获取到。大致就是这么的用法,但是没有个使用示例有点不好理解。

Future类
Future类位于tornado源码的concurrent模块中。下面是Future类里的一部分代码作为分析之用:

class Future(object):    def done(self):        return self._done    def result(self, timeout=None):        self._clear_tb_log()        if self._result is not None:            return self._result        if self._exc_info is not None:            raise_exc_info(self._exc_info)        self._check_done()        return self._result    def add_done_callback(self, fn):        if self._done:            fn(self)        else:            self._callbacks.append(fn)    def set_result(self, result):        self._result = result        self._set_done()    def _set_done(self):        self._done = True        for cb in self._callbacks:            try:                cb(self)            except Exception:                app_log.exception('exception calling callback %r for %r',                                  cb, self)        self._callbacks = None

Future类重要成员函数:

  • def done(self) : Future的_result成员是否被设置

  • def result(self, timeout=None) : 获取Future对象的结果

  • def add_done_callback(self, fn) : 添加一个回调函数fn给Future对象。如果这个Future对象已经done,则直接执行fn,否则将fn加入到Future类的一个成员列表中保存。

  • def_set_done(self) : 一个内部函数,主要是遍历列表,逐个调用列表中的callback函数,也就是前面 add_done_calback 加如来的。

  • def set_result(self, result) : 给Future对象设置result,并且调用_set_done。也就是说,当Future对象获得result后,所有add_done_callback加入的回调函数就会执行。

这里最终就是希望 future 调用 set_result ,然后就是执行回调函数。

自定义异步非阻塞Web框架

这节主要是想以源码的方式展示分析tornado是怎么实现异步非阻塞的。代码应该不是超的源码,只是借鉴了思路,做了很多简化。
下面是实现异步非阻塞的代码,主要是 select+socket :
https://www.cnblogs.com/wupeiqi/p/6536518.html

什么场景考虑使用Tornado

复杂的应用还是用django来开发。
如果要开发一个API的功能,或者其他的简单的工具、应用,也不用操作数据库。就可以用tornado或者是其他简单的框架。就不需要用django了。
可以选择的简单的框架还有Flask。

感谢各位的阅读!关于"Python中如何使用Tornado"这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,让大家可以学到更多知识,如果觉得文章不错,可以把它分享出去让更多的人看到吧!

0