浅谈Tornado框架

   tornado框架是一款相较于其他web framework处理服务器性能问题更加强健的轻量级的强大的Python web框架。tornado是一个可定制的非阻塞式异步加载框架,速度相当快。

大纲:

1,tornado入门

2,tornado表单和模板

3,模板扩展

4,数据库

5,异步web服务

6,编写安全应用

7,外部服务认证

8,部署tornado

tornado安装

     tornado的官网:http://www.tornadoweb.org/

        1,直接下载安装包并解压安装

         2,使用pip安装—>sudo pip install tornado

tornado简单的web服务

    Tornado是一个编写对HTTP请求响应的框架。下面是一个简单的Tornado应用的基础示例:

#引入需要的模块和包

import tornado.httpserver

import tornado.ioloop

import tornado.options

import tornado.web

from tornado.options import define, options

#定义段变量,这里默认为8000,可自己设置

define("port", default=8000, help="run on the given port", type=int)

#声明一个主页处理类,继承自RequestHandler

class IndexHandler(tornado.web.RequestHandler):

#重写父类的get方法,当请求为get方式时,执行此方法

def get(self):

greeting = self.get_argument('greeting', 'Hello')

#用write方法将需要的内容显示到网页上

self.write(greeting + ', friendly user!')

#程序运行的入口

if __name__ == "__main__":

#得到所有的输入参数和参数值

tornado.options.parse_command_line()

app = tornado.web.Application(handlers=[(r"/", IndexHandler)])

#创建一个HTTP对象

http_server = tornado.httpserver.HTTPServer(app)

#监听接口

http_server.listen(options.port)

#创建一个I/O实例并启动

tornado.ioloop.IOLoop.instance().start()

参数handler

    它是一个元组组成的列表,其中每个元组的第一个元素是一个用于匹配的正则表达式,第二个元素是一个RequestHanlder类。在hello.py中,我们只指定了一个正则表达式-RequestHanlder对,但你可以按你的需要指定任意多个。

表单和模板

Tornado自身提供了一个轻量级、快速并且灵活的模板语言在tornado.template模块中。

接下来是实例(写了比较重要的地方,其他的地方略去)

if __name__ == '__main__':

tornado.options.parse_command_line()

app = tornado.web.Application(

#路由映射

handlers=[(r'/', IndexHandler), (r'/poem', PoemPageHandler)],

#这里需要导入os模块配置模板路径,否则代码会报错

template_path=os.path.join(os.path.dirname(__file__), "templates")

)

http_server = tornado.httpserver.HTTPServer(app)

http_server.listen(options.port)

tornado.ioloop.IOLoop.instance().start()

此时需要创建一个模板文件夹,然后在此文件夹下创建需要的HTML文件就可以加载到程序中。当然需要在后台调用渲染才可以出效果

模板语法:

        先给大家介绍一种简单的应用语法-->{{花括号里放需要渲染的变量或对象属性}}

tornado会通过Python解释器将模板语法解释出来

填充表达式:


控制流语句:

if选择结构:

{%if 条件%}

{%endif%}

for循环结构:

{%for i in 变量%}

{%endfor%}

模板函数:

Tornado在所有模板中默认提供了一些简单便利的函数。它们包括:

escape(s)

替换字符串s中的&、<、>为他们对应的HTML字符。

url_escape(s)

使用urllib.quote_plus替换字符串s中的字符为URL编码形式。

json_encode(val)

将val编码成JSON格式。(在系统底层,这是一个对json库的dumps函数的调用。查阅相关的文档以获得更多关于该函数接收和返回参数的信息。)

squeeze(s)

过滤字符串s,把连续的多个空白字符替换成一个空格。

设置静态文件

第一步先配置路径:

app = tornado.web.Application(

handlers=[(r'/', IndexHandler), (r'/poem', MungedPageHandler)],

template_path=os.path.join(os.path.dirname(__file__), "templates"),

#配置静态文件路径

static_path=os.path.join(os.path.dirname(__file__), "static"),

debug=True

)

static的子目录作为static_path的参数。现在应用将以读取static目录下的filename.ext来响应诸如/static/filename.ext的请求,并在响应的主体中返回。

static_url可以逆向生成文件的路径,如:

<link rel="stylesheet" href="static_url('style.css')">

这个对static_url的调用生成了URL的值,并渲染输出类似下面的代码:

<link rel="stylesheet" href="/static/style.css">

模板扩展

模板的继承与替换 

意义:简单,便捷,大大增加了代码的复用性,增加了代码的可读性

Tornado通过extends和block语句支持模板继承{% extends "filename.html" %}。

比如,为了在新模板中扩展一个父模板(在这里假设为main.html),你可以这样使用:

{% extends "main.html" %}

然后,为了在子模板index.html中覆写{% block header %}{% end %}部分,你可以使用块的名字引用,并把任何你想要的内容放到其中。

{% block header %}{% end %}

{% block header %}

<**>hello world<**>

{% end %}

任何继承这个模板的文件都可以包含它自己的{% block header %}和{% end %},然后把一些不同的东西加进去。

UI模块:

UI模块是封装模板中包含的标记、样式以及行为的可复用组件。它所定义的元素通常用于多个模板交叉复用或在同一个模板中重复使用。模块本身是一个继承自Tornado的UIModule类的简单Python类,并定义了一个render方法。当一个模板使用{% module Foo(...) %}标签引用一个模块时,Tornado的模板引擎调用模块的render方法,然后返回一个字符串来替换模板中的模块标签。UI模块也可以在渲染后的页面中嵌入自己的JavaScript和CSS文件,或指定额外包含的JavaScript或CSS文件。你可以定义可选的embedded_javascript、embedded_css、javascript_files和css_files方法来实现这一方法

如:app = tornado.web.Application(

handlers=[(r'/', HelloHandler)],

template_path=os.path.join(os.path.dirname(__file__), 'templates'),

ui_modules={'Hello': HelloModule}

)

此时在网页中写入{% module Hello() %}就会渲染为HelloModule

数据库:

torndb是一个轻量级的基于MySQLdb封装的一个模块,从tornado3.0版本以后,其已经作为一个独立模块发行了。torndb依赖于MySQLdb模块,因此,在使用torndb模块时,要保证系统中已经有MySQLdb模块。

torndb模块仅提供了数据库的连接和表项操作指令,没有提供数据库的创建等操作命令。所以需要自己定义数据库操作的增删改查

(1)连接数据库

import torndb

db=torndb.Connection(hostaddress,database name,user,password)

(2)以get方式查询一行数据

>>> a=db.get('select * from query where id=1')

>>> a

{'queryc': 'dac', 'id': 1}

(3)以query方式查询一列数据

>>> a=db.query('select * from query')

>>> a

[{'id': 2, 'queryc': 'isca'}, {'id': 1, 'queryc': 'dac'}]

(4)执行无返回参数的命令

string='dac'

str='insert into query(id,queryc)values(%d,"%s")'%(1,string)

db.execute(exe)

另外,torndb还提供了插入、插入多行数据的函数,实际这些函数并不必要,直接调用无返回参数的命令即可完成。

而且,torndb模块并并没有给MySQLdb模块增加额外功能,建议在python中使用mysql数据库时,直接使用MySQLdb模块即可。

异步web服务

大部分Web应用(包括我们之前的例子)都是阻塞性质的,Tornado给了我们更好的方法来处理这种情况。应用程序在等待第一个处理完成的过程中,让I/O循环打开以便服务于其他客户端,直到处理完成时启动一个请求并给予反馈,而不再是等待请求完成的过程中挂起进程Tornado包含一个AsyncHTTPClient类,可以执行异步HTTP请求。

例:

from tornado.web import Application, RequestHandler

from tornado.ioloop import IOLoopimport tornado.web

from tornado.httpclient import AsyncHTTPClient

import json

class IndexHandler(RequestHandler):

 # 通过装饰器注解的方式,告诉tornado,这个处理方式是异步操作~开发人员要通过代码手工控制返回响应 

 @tornado.web.asynchronous def get(self): 

 # 创建一个异步请求对象 

 client = AsyncHTTPClient()

 # 异步请求对象发送请求,获取数据 

 client.fetch("https://suggest.taobao.com/sug?code=utf-8&q=iphoneX",\ callback=self.deal_response) 

 def deal_response(self, response): 

 content = response.body 

 json_ = json.loads(content) 

 print(json_, type(json_)) 

 self.write("

异步ok!

")   

 self.finish()

if __name__ == "__main__":  

  app = Application([(r"/", IndexHandler), ]) 

   app.listen(8000) 

   IOLoop.current().start()

异步装饰器和finish方法:

Tornado默认在函数处理返回时关闭客户端的连接。在通常情况下,这正是你想要的。但是当我们处理一个需要回调函数的异步请求时,我们需要连接保持开启状态直到回调函数执行完毕。你可以在你想改变其行为的方法上面使用@tornado.web.asynchronous装饰器来告诉Tornado保持连接开启,记住当你使用@tornado.web.asynchonous装饰器时,Tornado永远不会自己关闭连接。你必须在你的RequestHandler对象中调用finish方法来显式地告诉Tornado关闭连接。(否则,请求将可能挂起,浏览器可能不会显示我们已经发送给客户端的数据。)我们在函数的write后面调用了finish方法表示完成

tornado长轮询:

我自己的理解:长轮询就是客户端发送一个ajax请求,如果客户端未更新数据,一直等待,若更新数据则返回响应,然后再发一个请求等待。

所谓的"服务器推送"技术允许Web应用实时发布更新,同时保持合理的资源使用以及确保可预知的扩展。对于一个可行的服务器推送技术而言,它必须在现有的浏览器上表现良好。最流行的技术是让浏览器发起连接来模拟服务器推送更新。这种方式的HTTP连接被称为长轮询或Comet请求。

长轮询意味着浏览器只需启动一个HTTP请求,其连接的服务器会有意保持开启。浏览器只需要等待更新可用时服务器"推送"响应。当服务器发送响应并关闭连接后,(或者浏览器端客户请求超时),客户端只需打开一个新的连接并等待下一个更新

tornado与websocket:

Tornado在websocket模块中提供了一个WebSocketHandler类。这个类提供了和已连接的客户端通信的WebSocket事件和方法的钩子。当一个新的WebSocket连接打开时,open方法被调用,而on_message和on_close方法分别在连接接收到新的消息和客户端关闭时被调用。

此外,WebSocketHandler类还提供了write_message方法用于向客户端发送消息,close方法用于关闭连接。

class EchoHandler(tornado.websocket.WebSocketHandler):

def open(self):

self.write_message('connected!')

def on_message(self, message):

self.write_message(message)

正如你在我们的EchoHandler实现中所看到的,open方法只是使用WebSocketHandler基类提供的write_message方法向客户端发送字符串"connected!"。每次处理程序从客户端接收到一个新的消息时调用on_message方法,我们的实现中将客户端提供的消息原样返回给客户端。

编写安全:

很多时候,安全应用是以牺牲复杂度(以及开发者的头痛)为代价的。Tornado Web服务器从设计之初就在安全方面有了很多考虑,使其能够更容易地防范那些常见的漏洞。安全cookies防止用户的本地状态被其浏览器中的恶意代码暗中修改。此外,浏览器cookies可以与HTTP请求参数值作比较来防范跨站请求伪造攻击。在本章中,我们将看到使防范这些漏洞更简单的Tornado功能,以及使用这些功能的一个用户验证示例。

6.1 Cookie漏洞

许多网站使用浏览器cookies来存储浏览器会话间的用户标识。这是一个简单而又被广泛兼容的方式来存储跨浏览器会话的持久状态。不幸的是,浏览器cookies容易受到一些常见的攻击。本节将展示Tornado是如何防止一个恶意脚本来篡改你应用存储的cookies的。

6.1.1 Cookie伪造

有很多方式可以在浏览器中截获cookies。JavaScript和Flash对于它们所执行的页面的域有读写cookies的权限。浏览器插件也可由编程方法访问这些数据。跨站脚本攻击可以利用这些访问来修改访客浏览器中cookies的值。

6.1.2 安全Cookies

Tornado的安全cookies使用加密签名来验证cookies的值没有被服务器软件以外的任何人修改过。因为一个恶意脚本并不知道安全密钥,所以它不能在应用不知情时修改cookies。

6.1.2.1 使用安全Cookies

Tornado的set_secure_cookie()和get_secure_cookie()函数发送和取得浏览器的cookies,以防范浏览器中的恶意修改。为了使用这些函数,你必须在应用的构造函数中指定cookie_secret参数

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,271评论 5 466
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,725评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,252评论 0 328
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,634评论 1 270
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,549评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 47,985评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,471评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,128评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,257评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,233评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,235评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,940评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,528评论 3 302
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,623评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,858评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,245评论 2 344
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,790评论 2 339

推荐阅读更多精彩内容