tornado web 框架的认识

tornado 简介

1,概述

Tornado就是我们在 FriendFeed 的 Web 服务器及其常用工具的开源版本。Tornado 和现在的主流 Web 服务器框架(包括大多数 Python 的框架)有着明显的区别:它是非阻塞式服务器,而且速度相当快。得利于其 非阻塞的方式和对epoll的运用,Tornado 每秒可以处理数以千计的连接,因此 Tornado 是实时 Web 服务的一个 理想框架。我们开发这个 Web 服务器的主要目的就是为了处理 FriendFeed 的实时功能 ——在 FriendFeed 的应用里每一个活动用户都会保持着一个服务器连接。(关于如何扩容 服务器,以处理数以千计的客户端的连接的问题,请参阅The C10K problem)

Tornado代表嵌入实时应用中最新一代的开发和执行环境。 Tornado 包含三个完整的部分:

     (1)、Tornado系列工具, 一套位于主机或目标机上强大的交互式开发工具和使用程序;

     (2)、VxWorks 系统, 目标板上高性能可扩展的实时操作系统;

     (3)、可选用的连接主机和目标机的通讯软件包 如以太网、串行线、在线仿真器或ROM仿真器

2,tornado特点

Tornado的独特之处在于其所有开发工具能够使用在应用开发的任意阶段以及任何档次的硬件资源上。而且,完整集的Tornado工具可以使开发人员完全不用考虑与目标连接的策略或目标存储区大小。Tornado 结构的专门设计为开发人员和第三方工具厂商提供了一个开放环境。已有部分应用程序接口可以利用并附带参考书目,内容从开发环境接口到连接实现。Tornado包括强大的开发和调试工具,尤其适用于面对大量问题的嵌入式开发人员。这些工具包括C和C++源码级别的调试器,目标和工具管理,系统目标跟踪,内存使用分析和自动配置. 另外,所有工具能很方便地同时运行,很容易增加和交互式开发。

3,tornado模块索引

最重要的一个模块是web, 它就是包含了 Tornado 的大部分主要功能的 Web 框架。其它的模块都是工具性质的, 以便让 web 模块更加有用 后面的 Tornado 攻略 详细讲解了 web 模块的使用方法。

主要模块

  • web - FriendFeed 使用的基础 Web 框架,包含了 Tornado 的大多数重要的功能
  • escape - XHTML, JSON, URL 的编码/解码方法
  • database - 对 MySQLdb 的简单封装,使其更容易使用
  • template - 基于 Python 的 web 模板系统
  • httpclient - 非阻塞式 HTTP 客户端,它被设计用来和 web 及 httpserver 协同工作
  • auth - 第三方认证的实现(包括 Google OpenID/OAuth、Facebook Platform、Yahoo BBAuth、FriendFeed OpenID/OAuth、Twitter OAuth)
  • locale - 针对本地化和翻译的支持
  • options - 命令行和配置文件解析工具,针对服务器环境做了优化

底层模块

  • httpserver - 服务于 web 模块的一个非常简单的 HTTP 服务器的实现
  • iostream - 对非阻塞式的 socket 的简单封装,以方便常用读写操作
  • ioloop - 核心的 I/O 循环

tornado 框架使用

1,安装tornado

pip install tornado
源码安装:https://pypi.python.org/packages/source/t/tornado/tornado-4.3.tar.gz

2、先写一个入门级的代码吧,相信大家都能看懂,声明:tornado内部已经帮我们实现socket。

#!/usr/bin/env python
# -*- coding:utf-8 -*-
 
import tornado.web
import tornado.ioloop
 
class IndexHandler(tornado.web.RequestHandler):
 
    def get(self, *args, **kwargs):
        self.write("Hello World, My name is 赌神")
 
application = tornado.web.Application([
    (r'/index',IndexHandler),
])
 
if __name__ == "__main__":
    application.listen(8080)
    tornado.ioloop.IOLoop.instance().start()

第一步:执行脚本,监听 8080 端口

第二步:浏览器客户端访问 /index  -->  http://127.0.0.1:8080/index

第三步:服务器接受请求,并交由对应的类处理该请求

第四步:类接受到请求之后,根据请求方式(post / get / delete ...)的不同调用并执行相应的方法

第五步:然后将类的方法返回给浏览器

tornado 路由系统

在tornado web框架中,路由表中的任意一项是一个元组,每个元组包含pattern(模式)和handler(处理器)。当httpserver接收到一个http请求,server从接收到的请求中解析出url path(http协议start line中),然后顺序遍历路由表,如果发现url path可以匹配某个pattern,则将此http request交给web应用中对应的handler去处理。

由于有了url路由机制,web应用开发者不必和复杂的http server层代码打交道,只需要写好web应用层的逻辑(handler)即可。Tornado中每个url对应的是一个类

 1 #!/usr/bin/env python
 2 # -*- coding:utf-8 -*-
 3 __auth__ = "dushen"
 4  
 5 import tornado.web
 6 import tornado.ioloop
 7  
 8 class IndexHandler(tornado.web.RequestHandler):
 9  
10     def get(self, *args, **kwargs):
11         self.write("Hello World, My name is 赌神")
12  
13 class LoginHandler(tornado.web.RequestHandler):
14  
15     def get(self, *args, **kwargs):
16         self.write("<input type = 'text'>")
17  
18 class RegisterHandler(tornado.web.RequestHandler):
19  
20     def get(self, *args, **kwargs):
21         self.write("<input type = 'password'>")
22  
23 application = tornado.web.Application([
24     (r'/index/(?P<page>d*)',IndexHandler),  # 基础正则路由
25     (r'/login',LoginHandler),
26     (r'/register',RegisterHandler),
27 ])

观察所有的网页的内容,下面都有分页,当点击下一页后面的数字也就跟着变了,这种就可以用基础正则路由来做,下面我来给大家下一个网页分页的案例吧

  1 #!/usr/bin/env python
  2 # -*- coding:utf-8 -*-
  3 __auth__ = "zhangyanlin"
  4 
  5 import tornado.web
  6 import tornado.ioloop
  7 
  8 LIST_INFO = [
  9     {'username':'zhangyanlin','email':'133@164.com'}
 10 ]
 11 for i in range(200):
 12     temp = {'username':str(i)+"zhang",'email':str(i)+"@163.com"}
 13     LIST_INFO.append(temp)
 14 
 15 
 16 class Pagenation:
 17 
 18     def __init__(self,current_page,all_item,base_url):  #当前页 内容总数 目录
 19         try:
 20             page = int(current_page)
 21         except:
 22             page = 1
 23         if page < 1:
 24             page = 1
 25 
 26         all_page,c = divmod(all_item,5)
 27         if c > 0:
 28             all_page +=1
 29 
 30         self.current_page = page
 31         self.all_page = all_page
 32         self.base_url = base_url
 33 
 34     @property
 35     def start(self):
 36         return (self.current_page - 1) * 5
 37 
 38     @property
 39     def end(self):
 40         return self.current_page * 5
 41 
 42     def string_pager(self):
 43         list_page = []
 44         if self.all_page < 11:
 45             s = 1
 46             t = self.all_page + 1
 47         else:
 48             if self.current_page < 6:
 49                 s = 1
 50                 t = 12
 51             else:
 52                 if (self.current_page + 5) < self.all_page:
 53                     s = self.current_page-5
 54                     t = self.current_page + 6
 55                 else:
 56                     s = self.all_page - 11
 57                     t = self.all_page +1
 58 
 59         first = '<a href = "/index/1">首页</a>'
 60         list_page.append(first)
 61         # 当前页
 62         if self.current_page == 1:
 63             prev = '<a href = "javascript:void(0):">上一页</a>'
 64         else:
 65             prev = '<a href = "/index/%s">上一页</a>'%(self.current_page-1,)
 66         list_page.append(prev)
 67 
 68         #页码
 69         for p in range(s,t):
 70             if p== self.current_page:
 71                 temp = '<a class = "active" href = "/index/%s">%s</a>'%(p,p)
 72             else:
 73                 temp = '<a href = "/index/%s">%s</a>' % (p, p)
 74             list_page.append(temp)
 75 
 76 
 77 
 78         # 尾页
 79         if self.current_page == self.all_page:
 80             nex = '<a href = "javascript:void(0):">下一页</a>'
 81         else:
 82             nex = '<a href = "/index/%s">下一页</a>' % (self.current_page + 1,)
 83         list_page.append(nex)
 84 
 85         last = '<a href = "/index/%s">尾页</a>'%(self.all_page)
 86         list_page.append(last)
 87 
 88 
 89         #跳转
 90         jump = '''<input type="text"><a onclick = "Jump('%s',this);">GO</a>'''%('/index/')
 91         script = '''
 92             <script>
 93                 function Jump(baseUrl,ths){
 94                     var val = ths.previousElementSibling.value;
 95                     if (val.trim().length > 0){
 96                         location.href = baseUrl + val;
 97                     }
 98                 }
 99             </script>
100         '''
101         list_page.append(jump)
102         list_page.append(script)
103         str_page = "".join(list_page)
104 
105         return str_page
106 
107 class IndexHandler(tornado.web.RequestHandler):
108 
109     def get(self, page):
110         obj = Pagenation(page,len(LIST_INFO),'/index/')
111         current_list = LIST_INFO[obj.start:obj.end]
112         str_page = obj.string_pager()
113         self.render('index.html', list_info=current_list, current_page=obj.current_page, str_page=str_page)
114 
115 application = tornado.web.Application([
116     (r'/index/(?P<page>d*)',IndexHandler)
117 
118 ])
119 
120 
121 if __name__ == "__main__":
122     application.listen(8080)
123     tornado.ioloop.IOLoop.instance().start()
View Code
 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>Title</title>
 6     <style>
 7         .pager a{
 8             display: inline-block;
 9             padding: 5px 6px;
10             margin: 10px 3px;
11             border: 1px solid #2b669a;
12             text-decoration:none;
13 
14         }
15         .pager a.active{
16             background-color: #2b669a;
17             color: white;
18         }
19     </style>
20 </head>
21 <body>
22     <h3>显示数据</h3>
23     <table border="1">
24         <thead>
25             <tr>
26                 <th>用户名</th>
27                 <th>邮箱</th>
28             </tr>
29         </thead>
30         <tbody>
31             {% for line in list_info %}
32                 <tr>
33                     <td>{{line['username']}}</td>
34                     <td>{{line['email']}}</td>
35                 </tr>
36             {% end %}
37         </tbody>
38     </table>
39     <div class="pager">
40         {% raw str_page %}
41     </div>
42 </body>
43 </html>
前端html文件

注:两个文件必须放在同一个文件夹下,中间前端代码中有用到XSS攻击和模板语言,这两个知识点下面会详细解释

 tornado模板引擎

Tornao中的模板语言和django中类似,模板引擎将模板文件载入内存,然后将数据嵌入其中,最终获取到一个完整的字符串,再将字符串返回给请求者。

Tornado 的模板支持“控制语句”和“表达语句”,控制语句是使用 {% 和 %} 包起来的 例如 {% if len(items) > 2 %}。表达语句是使用 {{ 和 }} 包起来的,例如 {{ items[0] }}

控制语句和对应的 Python 语句的格式基本完全相同。我们支持 ifforwhile 和 try,这些语句逻辑结束的位置需要用 {% end %} 做标记。还通过 extends 和 block 语句实现了模板继承。这些在 template 模块 的代码文档中有着详细的描述。

注:在使用模板前需要在setting中设置模板路径:"template_path" : "views"

 1 settings = {
 2     'template_path':'views',             #设置模板路径,设置完可以把HTML文件放置views文件夹中
 3     'static_path':'static',              # 设置静态模板路径,设置完可以把css,JS,Jquery等静态文件放置static文件夹中
 4     'static_url_prefix': '/sss/',        #导入时候需要加上/sss/,例如<script src="/sss/jquery-1.9.1.min.js"></script>
 5     'cookie_secret': "asdasd",           #cookie生成秘钥时候需提前生成随机字符串,需要在这里进行渲染
 6     'xsrf_cokkies':True,                 #允许CSRF使用
 7 }
 8 application = tornado.web.Application([
 9     (r'/index',IndexHandler),
10 ],**settings)                           #需要在这里加载

 1、模板语言基本使用for循环,if..else使用,自定义UIMethod以UIModule

 1 #!/usr/bin/env python
 2 # -*- coding:utf-8 -*-
 3 import tornado.ioloop
 4 import tornado.web
 5 import uimodule as md
 6 import uimethod as mt
 7 
 8 INPUT_LIST = []
 9 class MainHandler(tornado.web.RequestHandler):
10     def get(self, *args, **kwargs):
11         name = self.get_argument('xxx',None)
12         if name:
13             INPUT_LIST.append(name)
14         self.render("index.html",npm = "NPM88888",xxoo = INPUT_LIST)
15 
16     def post(self, *args, **kwargs):
17         name = self.get_argument('xxx')
18         INPUT_LIST.append(name)
19         self.render("index.html", npm = "NPM88888", xxoo = INPUT_LIST)
20         # self.write("Hello, World!!!")
21 
22 settings = {
23     'template_path':'tpl',  # 模板路径的配置
24     'static_path':'statics',  # 静态文件路径的配置
25     'ui_methods':mt,        # 自定义模板语言
26     'ui_modules':md,        # 自定义模板语言
27 }
28 
29 #路由映射,路由系统
30 application = tornado.web.Application([
31     (r"/index",MainHandler),
32 ],**settings)
33 
34 
35 if __name__ == "__main__":
36     # 运行socket
37     application.listen(8000)
38     tornado.ioloop.IOLoop.instance().start()
start.py
1 from tornado.web import UIModule
2 from tornado import escape
3 
4 class custom(UIModule):
5 
6     def render(self, *args, **kwargs):
7         return "张岩林"
uimodule
1 def func(self,arg):
2     return arg.lower()
uimethod
 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>Title</title>
 6     <link type="text/css" rel="stylesheet" href="static/commons.css">
 7 </head>
 8 <body>
 9     <script src="static/zhang.js"></script>
10     <h1>Hello world</h1>
11     <h1>My name is zhangyanlin</h1>
12     <h1>输入内容</h1>
13     <form action="/index" method="post">
14         <input type="text" name="xxx">
15         <input type="submit" value="提交">
16     </form>
17     <h1>展示内容</h1>
18     <h3>{{ npm }}</h3>
19     <h3>{{ func(npm)}}</h3>
20     <h3>{% module custom() %}</h3>
21     <ul>
22         {% for item in xxoo %}
23             {% if item == "zhangyanlin" %}
24                 <li style="color: red">{{item}}</li>
25             {% else %}
26                 <li>{{item}}</li>
27             {% end %}
28         {% end %}
29     </ul>
30 </body>
31 </html>
index.html

2、母板继承

(1)、相当于python的字符串格式化一样,先定义一个占位符

 1 <!DOCTYPE html>
 2 <html>
 3 <head>
 4     <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
 5     <title>帅哥</title>
 6     <link href="{{static_url("css/common.css")}}" rel="stylesheet" />
 7     {% block CSS %}{% end %}
 8 </head>
 9 <body>
10 
11     <div class="pg-header">
12 
13     </div>
14     
15     {% block RenderBody %}{% end %}
16    
17     <script src="{{static_url("js/jquery-1.8.2.min.js")}}"></script>
18     
19     {% block JavaScript %}{% end %}
20 </body>
21 </html>
layout.html

(2)、再子板中相应的位置继承模板的格式

 1 {% extends 'layout.html'%}
 2 {% block CSS %}
 3     <link href="{{static_url("css/index.css")}}" rel="stylesheet" />
 4 {% end %}
 5 
 6 {% block RenderBody %}
 7     <h1>Index</h1>
 8 
 9     <ul>
10     {%  for item in li %}
11         <li>{{item}}</li>
12     {% end %}
13     </ul>
14 
15 {% end %}
16 
17 {% block JavaScript %}
18     
19 {% end %}
index.html

3、导入内容

1 <div>
2     <ul>
3         <li>张岩林帅</li>
4         <li>张岩林很帅</li>
5         <li>张岩林很很帅</li>
6     </ul>
7 </div>
View Code
 1 <!DOCTYPE html>
 2 <html>
 3 <head>
 4     <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
 5     <title>张岩林</title>
 6     <link href="{{static_url("css/common.css")}}" rel="stylesheet" />
 7 </head>
 8 <body>
 9 
10     <div class="pg-header">
11         {% include 'header.html' %}
12     </div>
13     
14     <script src="{{static_url("js/jquery-1.8.2.min.js")}}"></script>
15     
16 </body>
17 </html>
View Code
 1 在模板中默认提供了一些函数、字段、类以供模板使用:
 2  
 3 escape: tornado.escape.xhtml_escape 的別名
 4 xhtml_escape: tornado.escape.xhtml_escape 的別名
 5 url_escape: tornado.escape.url_escape 的別名
 6 json_encode: tornado.escape.json_encode 的別名
 7 squeeze: tornado.escape.squeeze 的別名
 8 linkify: tornado.escape.linkify 的別名
 9 datetime: Python 的 datetime 模组
10 handler: 当前的 RequestHandler 对象
11 request: handler.request 的別名
12 current_user: handler.current_user 的別名
13 locale: handler.locale 的別名
14 _: handler.locale.translate 的別名
15 static_url: for handler.static_url 的別名
16 xsrf_form_html: handler.xsrf_form_html 的別名

当你制作一个实际应用时,你会需要用到 Tornado 模板的所有功能,尤其是 模板继承功能。所有这些功能都可以在template 模块 的代码文档中了解到。(其中一些功能是在 web 模块中实现的,例如 UIModules

tornado cookie

 Cookie,有时也用其复数形式Cookies,指某些网站为了辨别用户身份、进行session跟踪而储存在用户本地终端上的数据(通常经过加密)。定义于RFC2109和2965都已废弃,最新取代的规范是RFC6265。(可以叫做浏览器缓存)

1、cookie的基本操作

 1 #!/usr/bin/env python
 2 # -*- coding:utf-8 -*-
 3    
 4 import tornado.ioloop
 5 import tornado.web
 6    
 7    
 8 class MainHandler(tornado.web.RequestHandler):
 9     def get(self):
10         print(self.cookies)              # 获取所有的cookie
11         self.set_cookie('k1','v1')       # 设置cookie
12         print(self.get_cookie('k1'))     # 获取指定的cookie
13         self.write("Hello, world")
14    
15 application = tornado.web.Application([
16     (r"/index", MainHandler),
17 ])
18    
19    
20 if __name__ == "__main__":
21     application.listen(8888)
22     tornado.ioloop.IOLoop.instance().start()

2、加密cookie(签名)

Cookie 很容易被恶意的客户端伪造。加入你想在 cookie 中保存当前登陆用户的 id 之类的信息,你需要对 cookie 作签名以防止伪造。Tornado 通过 set_secure_cookie 和 get_secure_cookie 方法直接支持了这种功能。 要使用这些方法,你需要在创建应用时提供一个密钥,名字为 cookie_secret。 你可以把它作为一个关键词参数传入应用的设置中

 1 #!/usr/bin/env python
 2 # -*- coding:utf-8 -*-
 3    
 4 import tornado.ioloop
 5 import tornado.web
 6     
 7 class MainHandler(tornado.web.RequestHandler):
 8     def get(self):
 9          if not self.get_secure_cookie("mycookie"):             # 获取带签名的cookie
10              self.set_secure_cookie("mycookie", "myvalue")      # 设置带签名的cookie
11              self.write("Your cookie was not set yet!")
12          else:
13              self.write("Your cookie was set!")
14 application = tornado.web.Application([
15     (r"/index", MainHandler),
16 ])
17    
18 if __name__ == "__main__":
19     application.listen(8888)
20     tornado.ioloop.IOLoop.instance().start()

签名Cookie的本质是:

写cookie过程:

    将值进行base64加密
    对除值以外的内容进行签名,哈希算法(无法逆向解析)
    拼接 签名 + 加密值

读cookie过程:

    读取 签名 + 加密值
    对签名进行验证
    base64解密,获取值内容

用cookie做简单的自定义用户验证,下面会写一个绝对牛逼的自定义session用户验证

 1 #!/usr/bin/python
 2 # -*- coding: UTF-8 -*-
 3 
 4 
 5 import tornado.web
 6 import tornado.ioloop
 7 container = {}
 8 # container = {
 9 #     # "第一个人的随机字符串":{},
10 #     # "第一个人的随机字符串":{'k1': 111, 'parents': '你'},
11 # }
12 
13 class Session:
14     def __init__(self, handler):
15         self.handler = handler
16         self.random_str = None
17 
18     def __genarate_random_str(self):
19         import hashlib
20         import time
21         obj = hashlib.md5()
22         obj.update(bytes(str(time.time()), encoding='utf-8'))
23         random_str = obj.hexdigest()
24         return random_str
25 
26     def __setitem__(self, key,value):
27         # 在container中加入随机字符串
28         # 定义专属于自己的数据
29         # 在客户端中写入随机字符串
30         # 判断,请求的用户是否已有随机字符串
31         if not self.random_str:
32             random_str = self.handler.get_cookie('__kakaka__')
33             if not random_str:
34                 random_str = self.__genarate_random_str()
35                 container[random_str] = {}
36             else:
37                 # 客户端有随机字符串
38                 if random_str in container.keys():
39                     pass
40                 else:
41                     random_str = self.__genarate_random_str()
42                     container[random_str] = {}
43             self.random_str = random_str # self.random_str = asdfasdfasdfasdf
44 
45         container[self.random_str][key] = value
46         self.handler.set_cookie("__kakaka__", self.random_str)
47 
48     def __getitem__(self,key):
49         # 获取客户端的随机字符串
50         # 从container中获取专属于我的数据
51         #  专属信息【key】
52         random_str =  self.handler.get_cookie("__kakaka__")
53         if not random_str:
54             return None
55         # 客户端有随机字符串
56         user_info_dict = container.get(random_str,None)
57         if not user_info_dict:
58             return None
59         value = user_info_dict.get(key, None)
60         return value
61 class BaseHandler(tornado.web.RequestHandler):
62     def initialize(self):
63         self.session = Session(self)
64 
65 class IndexHandler(BaseHandler):
66     def get(self):
67         if self.get_argument('u',None) in ['alex','eric']:
68             self.session['is_login'] = True
69             self.session['name'] = self.get_argument('u',None)
70 
71         else:
72             self.write('请登录')
73 
74 class ManagerHandler(BaseHandler):
75     def get(self):
76         s = Session(self)
77         val = self.session['is_login']
78         if val:
79             self.write(self.session['name'])
80         else:
81             self.write('失败')
自定义sesson实现用户登录

3、JavaScript操作Cookie

由于Cookie保存在浏览器端,所以在浏览器端也可以使用JavaScript来操作Cookie。

 1 /*
 2 设置cookie,指定秒数过期,
 3 name表示传入的key,
 4 value表示传入相对应的value值,
 5 expires表示当前日期在加5秒过期
 6  */
 7 
 8 function setCookie(name,value,expires){
 9     var temp = [];
10     var current_date = new Date();
11     current_date.setSeconds(current_date.getSeconds() + 5);
12     document.cookie = name + "= "+ value +";expires=" + current_date.toUTCString();
13 }

注:jQuery中也有指定的插件 jQuery Cookie 专门用于操作cookie,猛击这里

自定义session

cookie 和sesson的区别

1,cookie数据存放在客户的浏览器上,sesson数据存放在服务器上。

2,cookie不是很安全,别人可以分析存放本地的cookie并进行cookie欺骗,考虑到安全应当使用session

3,session会在一定的时间保存在服务器上。当访问量增多会比较粘你服务器的性能 考虑到减少服务器性能方面应当使用cookie

4,单个cookie保存的数据不能超过4k 横多浏览器都限制一个站点包村20个cookie

5,所以个人建议,将登录的信息的等重要的信息存放在session,其他信息如需保留可以存放在cookie中

本来这想新开一个帖子,但是还是把代码贴在这吧,有用到session验证的时候直接复制拿走就好

 1 #!/usr/bin/env python
 2 # -*- coding:utf-8 -*-
 3 import tornado.web
 4 import tornado.ioloop
 5 
 6 container = {}
 7 class Session:
 8     def __init__(self, handler):
 9         self.handler = handler
10         self.random_str = None
11 
12     def __genarate_random_str(self):
13         import hashlib
14         import time
15         obj = hashlib.md5()
16         obj.update(bytes(str(time.time()), encoding='utf-8'))
17         random_str = obj.hexdigest()
18         return random_str
19 
20     def __setitem__(self, key, value):
21         # 在container中加入随机字符串
22         # 定义专属于自己的数据
23         # 在客户端中写入随机字符串
24         # 判断,请求的用户是否已有随机字符串
25         if not self.random_str:
26             random_str = self.handler.get_cookie('__session__')
27             if not random_str:
28                 random_str = self.__genarate_random_str()
29                 container[random_str] = {}
30             else:
31                 # 客户端有随机字符串
32                 if random_str in container.keys():
33                     pass
34                 else:
35                     random_str = self.__genarate_random_str()
36                     container[random_str] = {}
37             self.random_str = random_str # self.random_str = asdfasdfasdfasdf
38 
39         container[self.random_str][key] = value
40         self.handler.set_cookie("__session__", self.random_str)
41 
42     def __getitem__(self, key):
43         # 获取客户端的随机字符串
44         # 从container中获取专属于我的数据
45         #  专属信息【key】
46         random_str =  self.handler.get_cookie("__session__")
47         if not random_str:
48             return None
49         # 客户端有随机字符串
50         user_info_dict = container.get(random_str,None)
51         if not user_info_dict:
52             return None
53         value = user_info_dict.get(key, None)
54         return value
55 
56 
57 class BaseHandler(tornado.web.RequestHandler):
58     def initialize(self):
59         self.session = Session(self)
自定义sesson

xss攻击和csrf请求伪造

跨站脚本攻击(Cross Site Scripting),为不和层叠样式表(Cascading Style Sheets, CSS)的缩写混淆,故将跨站脚本攻击缩写为XSS。恶意攻击者往Web页面里插入恶意Script代码,当用户浏览该页之时,嵌入其中Web里面的Script代码会被执行,从而达到恶意攻击用户的特殊目的。

tornado中已经为我们给屏蔽了XSS,但是当我们后端向前端写前端代码的时候传入浏览器是字符串,而不是形成代码格式。所以就需要一个反解,在传入模板语言中前面加一个raw,例如{{ raw zhangyanlin }},这样通俗的讲可能不太懂,写一段代码,可能就懂了

 1 class IndexHandler(tornado.web.RequestHandler):
 2     def get(self, *args, **kwargs):
 3         jump = '''<input type="text"><a onclick = "Jump('%s',this);">GO</a>'''%('/index/')
 4         script = '''
 5             <script>
 6                 function Jump(baseUrl,ths){
 7                     var val = ths.previousElementSibling.value;
 8                     if (val.trim().length > 0){
 9                         location.href = baseUrl + val;
10                     }
11                 }
12             </script>
13         '''
14         self.render('index.html',jump=jump,script=script)  #传入两个前端代码的字符串
View Code
 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>Title</title>
 6 </head>
 7 <body>
 8     <form action="/csrf" method="post">
 9         {% raw xsrf_form_html() %}
10         <p><input name="user" type="text" placeholder="用户"/></p>
11         <p><input name='pwd' type="text" placeholder="密码"/></p>
12         <input type="submit" value="Submit" />
13         <input type="button" value="Ajax CSRF" onclick="SubmitCsrf();" />
14     </form>
15     
16     <script src="/statics/jquery-1.12.4.js"></script>
17     <script type="text/javascript">
18 
19         function ChangeCode() {
20             var code = document.getElementById('imgCode');
21             code.src += '?';
22         }
23         function getCookie(name) {
24             var r = document.cookie.match("\b" + name + "=([^;]*)\b");
25             return r ? r[1] : undefined;
26         }
27 
28         function SubmitCsrf() {
29             var nid = getCookie('_xsrf');
30             $.post({
31                 url: '/csrf',
32                 data: {'k1': 'v1',"_xsrf": nid},
33                 success: function (callback) {
34                     // Ajax请求发送成功有,自动执行
35                     // callback,服务器write的数据 callback=“csrf.post”
36                     console.log(callback);
37                 }
38             });
39         }
40     </script>
41 </body>
42 </html>
csrf

简单来说就是在form验证里面生成了一段类似于自己的身份证号一样,携带着他来访问网页

tornado上传文件

上传文件这块可以分为两大类,第一类是通过form表单验证进行上传,还有一类就是通过ajax上传,下面就来介绍一下这两类

 1、form表单上传文件

 1 #!/usr/bin/env python
 2 # -*- coding:utf-8 -*-
 3 
 4 import tornado.web
 5 import tornado.ioloop
 6 import os
 7 
 8 class IndexHandler(tornado.web.RequestHandler):
 9     def get(self, *args, **kwargs):
10         self.render('index.html')
11 
12     def post(self, *args, **kwargs):
13         file_metas = self.request.files["filename"]     # 获取文件信息
14         for meta in file_metas:                         
15             file_name = meta['filename']                # 获得他的文件名字
16             file_names = os.path.join('static','img',file_name)
17             with open(file_names,'wb') as up:           # 打开本地一个文件
18                 up.write(meta['body'])                  # body就是文件内容,把他写到本地
19 
20 settings = {
21     'template_path':'views',
22     'static_path':'static',
23     'static_url_prefix': '/statics/',
24 }
25 
26 application = tornado.web.Application([
27     (r'/index',IndexHandler)
28 ],**settings)
29 
30 if __name__ == '__main__':
31     application.listen(8888)
32     tornado.ioloop.IOLoop.instance().start()
start.py
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>上传文件</title>
</head>
<body>
    <form action="/index" method="post" enctype="multipart/form-data">
        <input type="file" name = "filename">
        <input type="submit" value="提交">
    </form>
</body>
</html>
index.html

2.ajax上传文件

 1 <!DOCTYPE html>
 2 <html>
 3 <head lang="en">
 4     <meta charset="UTF-8">
 5     <title></title>
 6 </head>
 7 <body>
 8     <form id="my_form" name="form" action="/index" method="POST"  enctype="multipart/form-data" >
 9         <div id="main">
10             <input name="filename" id="my_file"  type="file" />
11             <input type="button" name="action" value="Upload" onclick="redirect()"/>
12             <iframe id='my_iframe' name='my_iframe' src=""  class="hide"></iframe>
13         </div>
14     </form>
15 
16     <script>
17         function redirect(){
18             document.getElementById('my_iframe').onload = Testt;
19             document.getElementById('my_form').target = 'my_iframe';
20             document.getElementById('my_form').submit();
21 
22         }
23         
24         function Testt(ths){
25             var t = $("#my_iframe").contents().find("body").text();
26             console.log(t);
27         }
28     </script>
29 </body>
30 </html>
html ifeame
 1 <!DOCTYPE html>
 2 <html>
 3 <head lang="en">
 4     <meta charset="UTF-8">
 5     <title></title>
 6 </head>
 7 <body>
 8     <input type="file" id="img" />
 9     <input type="button" onclick="UploadFile();" value="提交"/>
10 
11     <script src="/statics/jquery-1.12.4.js"></script>
12     <script>
13         function UploadFile(){
14             var fileObj = $("#img")[0].files[0];
15             var form = new FormData();
16             form.append("filename", fileObj);
17 
18             $.ajax({
19                 type:'POST',
20                 url: '/index',
21                 data: form,
22                 processData: false,  // tell jQuery not to process the data
23                 contentType: false,  // tell jQuery not to set contentType
24                 success: function(arg){
25                     console.log(arg);
26                 }
27             })
28         }
29     </script>
30 </body>
31 </html>
jquery 上传
 1 <!DOCTYPE html>
 2 <html>
 3 <head lang="en">
 4     <meta charset="UTF-8">
 5     <title></title>
 6 </head>
 7 <body>
 8     <input type="file" id="img" />
 9     <input type="button" onclick="UploadFile();" value="提交" />
10     <script>
11         function UploadFile(){
12             var fileObj = document.getElementById("img").files[0];
13 
14             var form = new FormData();
15             form.append("filename", fileObj);
16 
17             var xhr = new XMLHttpRequest();
18             xhr.open("post", '/index', true);
19             xhr.send(form);
20         }
21     </script>
22 </body>
23 </html>
XML提交
 1 #!/usr/bin/env python
 2 # -*- coding:utf-8 -*-
 3 
 4 import tornado.web
 5 import tornado.ioloop
 6 import os
 7 
 8 class IndexHandler(tornado.web.RequestHandler):
 9     def get(self, *args, **kwargs):
10         self.render('index.html')
11 
12     def post(self, *args, **kwargs):
13         file_metas = self.request.files["filename"]     # 获取文件信息
14         for meta in file_metas:
15             file_name = meta['filename']                # 获得他的文件名字
16             file_names = os.path.join('static','img',file_name)
17             with open(file_names,'wb') as up:           # 打开本地一个文件
18                 up.write(meta['body'])                  # body就是文件内容,把他写到本地
19 
20 settings = {
21     'template_path':'views',
22     'static_path':'static',
23     'static_url_prefix': '/statics/',
24 }
25 
26 application = tornado.web.Application([
27     (r'/index',IndexHandler)
28 ],**settings)
29 
30 if __name__ == '__main__':
31     application.listen(8888)
32     tornado.ioloop.IOLoop.instance().start()
start

注:下面所有的实例用相同的python代码都能实现,只需要改前端代码,python代码文件名为start.py

tornado生成随机字符串

 用python生成随机验证码需要借鉴一个插件,和一个io模块,实现起来也非常容易,当然也需要借鉴session来判断验证码是否错误,下面写一段用户登录验证带验证码的,再看下效果,插件必须和执行文件必须放在更目录下

  1 复制代码
  2 #!/usr/bin/env python
  3 # -*- coding:utf-8 -*-
  4 import tornado.web
  5 import tornado.ioloop
  6 
  7 container = {}
  8 class Session:
  9     def __init__(self, handler):
 10         self.handler = handler
 11         self.random_str = None
 12 
 13     def __genarate_random_str(self):
 14         import hashlib
 15         import time
 16         obj = hashlib.md5()
 17         obj.update(bytes(str(time.time()), encoding='utf-8'))
 18         random_str = obj.hexdigest()
 19         return random_str
 20 
 21     def __setitem__(self, key, value):
 22         # 在container中加入随机字符串
 23         # 定义专属于自己的数据
 24         # 在客户端中写入随机字符串
 25         # 判断,请求的用户是否已有随机字符串
 26         if not self.random_str:
 27             random_str = self.handler.get_cookie('__session__')
 28             if not random_str:
 29                 random_str = self.__genarate_random_str()
 30                 container[random_str] = {}
 31             else:
 32                 # 客户端有随机字符串
 33                 if random_str in container.keys():
 34                     pass
 35                 else:
 36                     random_str = self.__genarate_random_str()
 37                     container[random_str] = {}
 38             self.random_str = random_str # self.random_str = asdfasdfasdfasdf
 39 
 40         container[self.random_str][key] = value
 41         self.handler.set_cookie("__session__", self.random_str)
 42 
 43     def __getitem__(self, key):
 44         # 获取客户端的随机字符串
 45         # 从container中获取专属于我的数据
 46         #  专属信息【key】
 47         random_str =  self.handler.get_cookie("__session__")
 48         if not random_str:
 49             return None
 50         # 客户端有随机字符串
 51         user_info_dict = container.get(random_str,None)
 52         if not user_info_dict:
 53             return None
 54         value = user_info_dict.get(key, None)
 55         return value
 56 
 57 
 58 class BaseHandler(tornado.web.RequestHandler):
 59     def initialize(self):
 60         self.session = Session(self)
 61 
 62 
 63 class LoginHandler(BaseHandler):
 64     def get(self, *args, **kwargs):
 65         self.render('login.html' ,state = "")
 66 
 67     def post(self, *args, **kwargs):
 68         username = self.get_argument('username')
 69         password = self.get_argument('password')
 70         code =self.get_argument('code')
 71         check_code = self.session['CheckCode']
 72         if username =="zhangyanlin" and password == "123" and code.upper() == check_code.upper():
 73             self.write("登录成功")
 74         else:
 75             self.render('login.html',state = "验证码错误")
 76 
 77 class CheckCodeHandler(BaseHandler):
 78     def get(self, *args, **kwargs):
 79         import io
 80         import check_code
 81         mstream = io.BytesIO()
 82         img,code = check_code.create_validate_code()
 83         img.save(mstream,"GIF")
 84         self.session['CheckCode'] =code
 85         self.write(mstream.getvalue())
 86 
 87 
 88 settings = {
 89     'template_path':'views',
 90     'cookie_secret': "asdasd",
 91 }
 92 
 93 application = tornado.web.Application([
 94     (r'/login',LoginHandler),
 95     (r'/check_code',CheckCodeHandler)
 96 ],**settings)
 97 
 98 if __name__ == '__main__':
 99     application.listen(8888)
100     tornado.ioloop.IOLoop.instance().start()
start.py
 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>验证码</title>
 6 </head>
 7 <body>
 8     <form action="/login" method="post">
 9         <p>用户名: <input type="text" name="username"> </p>
10         <p>密码: <input type="password" name="password"> </p>
11         <p>验证码: <input type="text" name="code"><img src="/check_code" onclick="ChangeCode();" id = "checkcode"></p>
12         <input type="submit" value="submit"> <span>{{state}}</span>
13     </form>
14 <script type="text/javascript">  //当点击图片的时候,会刷新图片,这一段代码就可以实现
15     function ChangeCode() {
16         var code = document.getElementById('checkcode');
17         code.src += "?";
18     }
19 </script>
20 </body>
21 </html>
login.html

 插件下载地址:猛击这里

 

原文地址:https://www.cnblogs.com/pythonxiaokang/p/5720587.html