Python自动化开发学习-Tornado

Tornado 基本操作

讲师的博客:
白话tornado源码系列5篇,主要是源码剖析暂时不需要知道那么多。只要看下第一篇就好:
https://www.cnblogs.com/wupeiqi/tag/Tornado/
Web框架之Tornado:
https://www.cnblogs.com/wupeiqi/p/5702910.html

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.web

class 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()

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

<body>
<h1>Hello World</h1>
<h3>{{ k1 }}</h3>
{% if k2 == ‘v2‘ %}
    <h3>k2 == v2</h3>
{% else %}}
    <h2>k2 != v2</h2>
{% end %}
</body>

加载配置

上面的前端代码,最好是统一保存在某个目录里,比如新建个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

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

<body>
<form method="POST" action="/baidu">
    <input type="text" name="wd" />
    <input type="submit" value="百度一下" />
</form>
</body>

后端的代码:

import tornado.web

class 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,而不是文件夹真正的名称:

<img src="static/test.jpg" />

这里前端引用的是必须用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.py
def test1(self):  # 这里的self不能去掉
    return "TEST1"

def test2(self):
    return "TEST2"

# ui_module.py
from tornado.web import UIModule
from tornado import escape

class Test(UIModule):
    def render(self, *args, **kwargs):
        return escape.xhtml_escape(‘<h3>UI Module TEST</h3>‘)

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

import tornado.web
import ui_methods as mt
import ui_modules as md

class 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()

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

<body>
<h1>UI Method</h1>
<p>{{ test1() }}</p>
<p>{{ test2() }}</p>
<h1>UI Module</h1>
{% module Test() %}
</body>

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(‘<h3>UI Module TEST</h3>‘)

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

CSRF

首先在settings里启用csrf:

settings = {
    "xsrf_cookies": True,
}

在 form 中使用

<form action="/new_message" method="post">
  {% raw xsrf_form_html() %}
  <input type="text" name="message"/>
  <input type="submit" value="提交"/>
</form>

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

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

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 + ")"));
    }});
};

上传文件

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

<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
    <title>上传文件</title>
</head>
<body>
    <form id="my_form" name="form" action="/index" method="POST"  enctype="multipart/form-data" >
        <input name="fff" id="my_file"  type="file" />
        <input type="submit" value="提交"  />
    </form>
</body>
</html>

接收上传文件:

import tornado.web

class 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 gen
from tornado.concurrent import Future

class 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。

原文地址:http://blog.51cto.com/steed/2326739

时间: 2024-08-29 09:52:58

Python自动化开发学习-Tornado的相关文章

Python自动化开发学习12-MariaDB

关系型数据库 主流的关系型数据库大概有下面这些: Oracle : 甲骨文公司的企业级的数据库 SQL Server : 微软的 MySQL : 免费的数据库,现在也属于Oracle的旗下产品 MariaDB : 开源的数据库,MySQL的一个分支 PostgreSQL : 也是开源的 SQLite : 一款轻量级的数据库 DB2 : IBM的 RDBMS 术语 RDBMS(Relational Database Management System)即关系数据库管理系统,在开始之前,先了解下RD

Python自动化开发学习18-Django基础篇

自定义Web框架 跟着老师一点一点完善代码和文档结构,最终一个Web框架的雏形会显现出来,然后引出之后要学习完善的的Web框架. Web框架的本质 Web服务端的本质就是一个socket服务端,而我们的浏览器就是socket客户端.浏览器发送的请求就是一次socket请求.一次请求获得一次响应,然后就断开,这是一个短连接.下面是一个服务端的python代码,直接用socket,实现一个简单的Hello World: import socket def handle_request(conn):

Python自动化开发学习19-Django

接下来,我们把Django分为视图(View).路由系统(URL).ORM(Model).模板(Templates )这4块进行学习. 视图 提交数据 上节课已经用过 request.POST.get() 获取提交的数据了,现在来看看有多选框的情况,多选的话应该要提交多个数据.先写一个有单选.多选.下拉列表的html: <body> <form action="/choice/" method="post"> <p> 性别: &l

python自动化开发学习-9 socket网络编程

一. 简介 python提供了两个级别访问的网络服务: 低级别的网络服务支持节本的socket,它提供了标准的BSD sockets API,可以访问底层操作系统socket接口的全部方法. 高级别的网络服务模块socketserver,它提供了服务器中心类,可以简化网络服务器的开发. socket介绍 socket通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄,应用程序通常通过"套接字"向网络发出请求或者应答网络请求. socket起源于Unix

Python自动化开发学习4-3

JSON 和 pickle 序列化:把数据对象变成字符串的形式,这样可以保存在文件中.反之就是反序列化 python自带的str()可以完成序列化,然后eval()可以反序列化,但是我们先把他们忘记.不知道适用范围是多大. 我们用json的规范来做序列化和反序列化. import json dica = {'name':'Alice',         'age':18,         'from':'ShangHai'         } stra = json.dumps(dica)  #

Python自动化开发学习5

模块 在模块中,我们可以定义变量.函数,还有类(这个还没学到).总之应该就是所有的代码.先建一个文件,取名为module,py,内容如下: # 一下是module.py的内容 string = "This is module,py" def say_hi():     print("Hi") def test():     return "test in module.py" 在上面的模块中我们定义了1个变量和2个函数,现在我们要在另外一个文件中

Python自动化开发学习14

Web的三个层次 网页设计思路是把网页分成三个层次,即:结构层(HTML).表示层(CSS).行为层(Javascript).形象的比喻,先是HTML捏了一个人,然后CSS则是给人穿上衣服,最后通过JS让人动起来. Web服务的本质 对于所有的Web应用,本质上其实就是一个socket服务端,用户的浏览器其实就是一个socket客户端.用户向服务器发送一个请求.然后服务器响应,将数据和格式发回给客户端,然后断开这个连接.客户端收到返回的数据后,通过浏览器将数据按照一定的格式呈现出来.整个过程就是

Python自动化开发学习15-JavaScript和DOM

初识JavaScript JavaScript(后面都简写为js)是一门独立的语言.浏览器本身就具有js解释器. js的存在形式 和css类似,js代码要放在<script>标签中.同样和css类似,还可以写在一个js文件中,比如文件名就叫 commons.js ,然后在head中引入 <script src="commons.js"></script> ,src的值就是js的文件路径.<script type="text/javas

Python自动化开发学习16-前端内容综合进阶

css补充 这里再补充几个css的知识点,下面会用到 最小宽度-min-width 设置元素的最小宽度.举例说明,比如设置一个百分比的宽度,那么元素的宽度的像素值是会随着页面大小而变化的.如果设置一个最小的像素宽度,那么当变化到最小值之后,不会继续变小.在下面的例子中,会出现滚动条,保证元素的宽度: <body> <div style="height: 80px;width: 100%;background-color: blue;min-width: 800px;"