Tornado框架

Tornado解析

Tornado是使用Python编写的一个强大的、可扩展的Web服务器。它在处理严峻的网络流量时表现得足够强健,但却在创建和编写时有着足够的轻量级,并能够被用在大量的应用和工具中。

我们现在所知道的Tornado是基于Bret Taylor和其他人员为FriendFeed所开发的网络服务框架,当FriendFeed被Facebook收购后得以开源。不同于那些最多只能达到10,000个并发连接的传统网络服务器,Tornado在设计之初就考虑到了性能因素,旨在解决C10K问题,这样的设计使得其成为一个拥有非常高性能的框架。此外,它还拥有处理安全性、用户验证、社交网络以及与外部服务(如数据库和网站API)进行异步交互的工具。

快速入手

  1、安装Tornado

1)pip install tornado

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

  

  2、首个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")  # 可以返回字符串
    # self.render("s1.html")  # 这个可以返回html
    # self.redircet("www.baidu.com")  # 跳转到某个url
  
application = tornado.web.Application([
    (r"/index", MainHandler),
])
  
  
if __name__ == "__main__":
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()

示例:

# !/usr/bin/env python
# -*- coding:utf-8 -*-
import tornado.ioloop
import tornado.web


class LoginHandler(tornado.web.RequestHandler):

    def get(self, *args, **kwargs):
        # self.render('login.html', state="")  # 返回HTML页面
        self.render('index.html', k1='123')

    def post(self, *args, **kwargs):
        user = self.get_argument('username')
        pwd = self.get_argument('password')
        if user == "alex" and pwd == "123":
            self.redirect("http://www.baidu.com")  # 跳转百度的url

        else:
            self.write('登录失败,请重新输入')  # 返回字符串


settings = {
    'template_path': 'views',
}

application = tornado.web.Application([
    # (r"/login", LoginHandler),
    (r"/index", LoginHandler),
], **settings)


if __name__ == "__main__":
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()
start.py
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<form method="post" action="/login">
    <input id="user" type="text" name="username" />
    <input id="pwd" type="password" name="password" />
    <input type="submit" value="登录"  />
</form>


</body>
</html>
login.html
<!DOCTYPE html>

<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>{{k1}}</h1>
<script>
    alert({{ k1}})
</script>
</body>
</html>
index.html

基本上所有的Web框架都是以下的流程(以Tornado为例):  

准备阶段

  加载配置文件

  加载路由映射  application = tornado.web.Application([(r"/index", MainHandler),])

  创建socket  

循环阶段

  类似socket Server不断的循环监听文件句柄,当有请求过来的时候,根据用户的请求方法来来判断是什么请求,在通过反射来执行相应的函数或类

流程:

第一步:执行脚本,监听 8888 端口
第二步:浏览器客户端访问 /index  -->  http://127.0.0.1:8888/index
第三步:服务器接受请求,并交由对应的类处理该请求
第四步:类接受到请求之后,根据请求方式(post(请求) / get(获取) / delete(删除) / pot(设置))的不同调用并执行相应的方法
第五步:方法返回值的字符串内容发送浏览器

  

  3、application配置

application = tornado.web.Application([(r"/index", MainHandler),])

内部在执行的时候执行了两个方法__init__方法和self.add_handlers(".*$", handlers)方法  

add_handlers默认传输的".*$" 就是www,他内部生成的路由映射的时候相当于(二级域名的方式)下图:

 

 如果匹配的是shuaige他会去"shuaige"里去找对应关系,如果没有匹配默认就去.*,他这个就类似Django中的URL分类

application = tornado.web.Application([
    (r"/index", MainHandler),
])

application.add_handlers("shuaige.com",([
    (r"/index", MainHandler),
])
)

  

路由系统

 路由系统其实就是 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 Shuaige(tornado.web.RedirectHandler):
    def get(self):
        self.write("This is shuaige web site,hello!")

application = tornado.web.Application([
    (r"/index", MainHandler),
])

application.add_handlers("kongweiqi.com",([
    (r"/index", kongweiqi),
])
)


if __name__ == "__main__":
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()
View Code

路由系统可以分为几类:

  • 静态路由

  • 动态路由

  • 请求方法路由

  • 二级路由

  1、静态路由

@root.route('/hello/')
def index():
    return template('<b>Hello {{name}}</b>!', name="weiqi")

并且可以绑定多个装饰器如代码(当你访问hello的时候回执行下面的函数,当你访问index的时候也会执行下面的index函数) 

@root.route('/index/')
@root.route('/hello/')
def index():
    #return "Hello World"
    return template('<b style="background-color:red">Hello {{name}}</b>!', name="Tim")

root.run(host='localhost', port=8080)
View Code

  2、动态路由(支持正则)

@root.route('/wiki/<pagename>')
def callback(pagename):
    ...

#当请求过来之后@root.route('/wiki/<pagename>') 这个pagename值就会自动把值赋值给def callback(pagename):的参数
View Code
@root.route('/wiki/<pagename>')
def callback(pagename):
    ...
 
@root.route('/object/<id:int>')
def callback(id):
    ...
 
@root.route('/show/<name:re:[a-z]+>')
def callback(name):
    ...
 
@root.route('/static/<path:path>')
def callback(path):
    return static_file(path, root='static')
正则例子

  3、请求方法路由

在http访问请求中有很多请求方法比如get、post、delete等

如果使用了@root.get就表示这个装饰器下的函数只接收get请求!

@root.route('/hello/', method='POST')
def index():
    ...
 
@root.get('/hello/')
def index():
    ...
 
@root.post('/hello/')
def index():
    ...
 
@root.put('/hello/')
def index():
    ...
 
@root.delete('/hello/')
def index():
    ...
View Code

  4、二级路由  

多个APP把不同app下的请求转发到不同的app下面进行处理

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from bottle import template, Bottle
from bottle import static_file
root = Bottle()
 
@root.route('/hello/')
def index():
    return template('<b>Root {{name}}</b>!', name="Alex")
 
from framwork_bottle import app01
from framwork_bottle import app02
 
root.mount('app01', app01.app01)
root.mount('app02', app02.app02)
 
root.run(host='localhost', port=8888)
indexpy
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from bottle import template, Bottle

app01 = Bottle()

@app01.route('/hello/', method='GET')
def index():
    return template('<b>App01</b>!'
app01
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from bottle import template, Bottle

app02 = Bottle()


@app02.route('/hello/', method='GET')
def index():
    return template('<b>App02</b>!')
app02

模板引擎

  1、解析

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

Tornado 的模板支持“控制语句”和“表达语句”,

控制语句是使用 {% 和 %} 包起来的 例如 {% if len(items) > 2 %};

表达语句是使用 {{ 和 }} 包起来的,例如 {{ items[0] }};

控制语句和对应的 Python 语句的格式基本完全相同。我们支持 ifforwhile 和 try

这些语句逻辑结束的位置需要用 {% end %} 做标记。还通过 extends 和 block 语句实现了模板继承。这些在 template 模块 的代码文档中有着详细的描述。

  2、目录结构

#!/usr/bin/env python
# -*- coding:utf-8 -*-
  
import tornado.ioloop
import tornado.web
  
  
class MainHandler(tornado.web.RequestHandler):
    def get(self):  # 用get 的方式接受
        self.render('home/index.html')  # 可以返回html
        #self.write("Hello world")  # 可以返回字符串
    
    def post(self, *args, **kwargs):  # 用post方式接受
        name = self.get_argument('xxx')
        print(name)
        self.render('index.html')
 
# 模板路径的配置 
settings = {
    'template_path': 'template',    # 配置文件路径,设置完可以把HTML文件放置template
    'static_path': 'static',        # 静态文件的配置
#     'static_url_prefix':'/sss/'   # 静态文件的前缀,在静态文件夹前面加上这个前缀 
    'cookie_secret': "asdasd",      # cookie生成秘钥时候需提前生成随机字符串,需要在这里进行渲染
    'xsrf_cokkies':True,            # 允许CSRF使用
}
  
  # 路由系统
application = tornado.web.Application([
    (r"/index", MainHandler),  # 匹配url和类
], **settings)  # 接受配置
  
  
if __name__ == "__main__":
    application.listen(80)
    tornado.ioloop.IOLoop.instance().start()
主文件index.py
<!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" />
    {% block CSS %}{% end %}
</head>
<body>

    <div class="pg-header">

    </div>
    
    {% block RenderBody %}{% end %}
   
    <script src="{{static_url("js/jquery-1.8.2.min.js")}}"></script>
    
    {% block JavaScript %}{% end %}
</body>
</html>

layout.html
layout.html
{% extends 'layout.html'%}
{% block CSS %}
    <link href="{{static_url("css/index.css")}}" rel="stylesheet" />
{% end %}

{% block RenderBody %}
    <h1>Index</h1>

    <ul>
    {%  for item in li %}
        <li>{{item}}</li>
    {% end %}
    </ul>

{% end %}

{% block JavaScript %}
    
{% end %}

index.html
index.html
{% extends 'layout.html'%}
{% block CSS %}
    <link href="{{static_url("css/index.css")}}" rel="stylesheet" />
{% end %}

{% block RenderBody %}
    <h1>Index</h1>

    <ul>
    {%  for item in li %}
        <li>{{item}}</li>
    {% end %}
    </ul>

{% end %}

{% block JavaScript %}
    
{% end %}
for循环

  3、语法

<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
    <h1>1、单值</h1>
      {{name}}
    #应用后端返回的数据

    <h1>2、单行Python代码</h1>
        % s1 = "hello"
        {{ s1 }}
    #'%s1 这里设置一个变量,我们可以在html调用和python类似'

    <h1>3、Python代码块</h1>
    <%
        # python块代码
        name = name.title().strip()
        if name == "shuaige":
            name="luotianshuai"
    %>

    <h1>4、Python、Html混合</h1>

    % if True:
        <span>{{name}}</span>
    % end
    <ul>
      % for item in name:
        <li>{{item}}</li>
      % end
    </ul>

</body>
</html>
View Code

  4、函数

 include(sub_template, **variables)

# 导入其他模板文件
 
% include('header.tpl', title='Page Title')
Page Content
% include('footer.tpl')

rebase(name, **variables)

<html>
<head>
  <title>{{title or 'No title'}}</title>
</head>
<body>
  {{!base}}
</body>
</html>

导入母版

% rebase('base.tpl', title='Page Title')
<p>Page Content ...</p>
defined(name) #检查当前变量是否已经被定义,已定义True,未定义False
get(name, default=None) #获取某个变量的值,不存在时可设置默认值
setdefault(name, default) #如果变量不存在时,为变量设置默认值

母版继承

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

<!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" />
    {% block CSS %}{% end %}
</head>
<body>

    <div class="pg-header">

    </div>
    
    {% block RenderBody %}{% end %}
   
    <script src="{{static_url("js/jquery-1.8.2.min.js")}}"></script>
    
    {% block JavaScript %}{% end %}
</body>
</html>
layout.html

②再子板中相应的位置继承模板的格式

{% extends 'layout.html'%}
{% block CSS %}
    <link href="{{static_url("css/index.css")}}" rel="stylesheet" />
{% end %}

{% block RenderBody %}
    <h1>Index</h1>

    <ul>
    {%  for item in li %}
        <li>{{item}}</li>
    {% end %}
    </ul>

{% end %}

{% block JavaScript %}
    
{% end %}
index.html

导入内容

<div>
    <ul>
        <li>帅</li>
        <li>很帅</li>
        <li>非常帅</li>
    </ul>
</div>
content.html
<!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>
index.html

在模板中默认提供了一些函数、字段、类以供模板使用:

  • escapetornado.escape.xhtml_escape 的別名

  • xhtml_escapetornado.escape.xhtml_escape 的別名

  • url_escapetornado.escape.url_escape 的別名

  • json_encodetornado.escape.json_encode 的別名

  • squeezetornado.escape.squeeze 的別名

  • linkifytornado.escape.linkify 的別名

  • datetime: Python 的 datetime 模组

  • handler: 当前的 RequestHandler 对象

  • requesthandler.request 的別名

  • current_userhandler.current_user 的別名

  • localehandler.locale 的別名

  • _handler.locale.translate 的別名

  • static_url: for handler.static_url 的別名

  • xsrf_form_htmlhandler.xsrf_form_html 的別名

  5、自定义模板

Tornado默认提供的这些功能其实本质上就是 UIMethod 和 UIModule,我们也可以自定义从而实现类似于Django的simple_tag的功能。

 1、定义

def tab(self):
    return 'UIMethod'
uimethods.py
#!/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>wupeiqi</h1>')
        #return escape.xhtml_escape('<h1>wupeiqi</h1>')
uimodules.py

2、注册

#!/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()
main.py

3、使用

<!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>
index.html

  6、 公共组件

由于Web框架就是用来【接收用户请求】-> 【处理用户请求】-> 【响应相关内容】,对于具体如何处理用户请求,开发人员根据用户请求来进行处理,而对于接收用户请求和相应相关的内容均交给框架本身来处理,其处理完成之后将产出交给开发人员和用户。

【接收用户请求】

当框架接收到用户请求之后,将请求信息封装在Bottle的request中,以供开发人员使用

【响应相关内容】

当开发人员的代码处理完用户请求之后,会将其执行内容相应给用户,相应的内容会封装在Bottle的response中,然后再由框架将内容返回给用户

所以,公共组件本质其实就是为开发人员提供接口,使其能够获取用户信息并配置响应内容。

request.headers
#请求头信息,可以通过请求头信息来获取相关客户端的信息
 
request.query
#get请求信息,如果用户访问时这样的:http://127.0.0.1:8000/?page=123就必须使用request.query  使用GET方法是无法取到信息的
 
request.forms
#post请求信息
 
request.files
#上传文件信息
 
request.params
#get和post请求信息,他是GET和POST的总和,其实他内部调用了request.get request.forms
 
request.GET
#get请求信息
 
request.POST
#post和上传信息,上传文件信息,和post信息
 
request.cookies
#cookie信息
     
request.environ
#环境相关相关,如果上面的这些请求信息没有满足你的需求,就在这里找!
View Code

实用功能

  1、静态文件

对于静态文件,可以配置静态文件的目录和前段使用时的前缀,并且Tornaodo还支持静态文件缓存。

静态文件缓存:

<link href="{{static_url('css/index.css')}}" rel="stylesheet" />
"""
在Tornado中写成变量形式的主要是为了以后扩展方便,还有一个缓存的功能
"""

原理:

拿一个静态文件来说:/static/commons.js如果用Tornado封装后,类似于给他加了一个版本号/static/commons.js?v=sldkfjsldf123

当客户端访问过来的时候会携带这个值,如果发现没变客户端缓存的静态文件直接渲染就行了,不必再在服务器上下载一下静态文件了。

#!/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()
main.py
<!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>
index.html

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、xss

跨站脚本攻击

恶意攻击者往Web页面里插入恶意Script代码,当用户浏览该页之时,嵌入其中Web里面的Script代码会被执行,从而达到恶意攻击用户的特殊目的。

class IndexHandler(tornado.web.RequestHandler):
    def get(self, *args, **kwargs):
        jump = '''<input type="text"><a onclick = "Jump('%s',this);">GO</a>'''%('/index/')
        script = '''
            <script>
                function Jump(baseUrl,ths){
                    var val = ths.previousElementSibling.value;
                    if (val.trim().length > 0){
                        location.href = baseUrl + val;
                    }
                }
            </script>
        '''
        self.render('index.html',jump=jump,script=script)  #传入两个前端代码的字符串
start.py
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        .pager a{
            display: inline-block;
            padding: 5px;
            margin: 3px;
            background-color: #00a2ca;
        }
        .pager a.active{
            background-color: #0f0f0f;
            color: white;
        }
    </style>
</head>
<body>
    <div class="pager">
        {% raw jump %}
        {% raw script%}
    </div>
</body>
</html>    
index.py

  3、CSRF跨站请求伪造

跨站伪造请求(Cross-site request forgery)

settings = {
    "xsrf_cookies": True,
}
application = tornado.web.Application([
    (r"/", MainHandler),
    (r"/login", LoginHandler),
], **settings)
配置
<form action="/new_message" method="post">
  {{ xsrf_form_html() }}
  <input type="text" name="message"/>
  <input type="submit" value="Post"/>
</form>
普通表单使用
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使用
ajax

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

 Tornado 有内建的 XSRF 的防范机制,要使用此机制,你需要在应用配置中加上 xsrf_cookies

设定:xsrf_cookies=True,再来写一段代码,来表示一下:

#!/usr/bin/env python
# -*- coding:utf-8 -*-

import tornado.web
import tornado.ioloop

class CsrfHandler(tornado.web.RequestHandler):

    def get(self, *args, **kwargs):
        self.render('csrf.html')

    def post(self, *args, **kwargs):
        self.write('张岩林已经收到客户端发的请求伪造')


settings = {
    'template_path':'views',
    'static_path':'statics',
    'xsrf_cokkies':True,        # 重点在这里,往这里看
}

application = tornado.web.Application([
    (r'/csrf',CsrfHandler)
],**settings)

if __name__ == "__main__":
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()
start.py
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form action="/csrf" method="post">
        {% raw xsrf_form_html() %}
        <p><input name="user" type="text" placeholder="用户"/></p>
        <p><input name='pwd' type="text" placeholder="密码"/></p>
        <input type="submit" value="Submit" />
        <input type="button" value="Ajax CSRF" onclick="SubmitCsrf();" />
    </form>
    
    <script src="/statics/jquery-1.12.4.js"></script>
    <script type="text/javascript">

        function ChangeCode() {
            var code = document.getElementById('imgCode');
            code.src += '?';
        }
        function getCookie(name) {
            var r = document.cookie.match("\b" + name + "=([^;]*)\b");
            return r ? r[1] : undefined;
        }

        function SubmitCsrf() {
            var nid = getCookie('_xsrf');
            $.post({
                url: '/csrf',
                data: {'k1': 'v1',"_xsrf": nid},
                success: function (callback) {
                    // Ajax请求发送成功有,自动执行
                    // callback,服务器write的数据 callback=“csrf.post”
                    console.log(callback);
                }
            });
        }
    </script>
</body>
</html>
csrf.py

在form验证里面生成了一段类似于自己的身份标识一样,携带着这个标识来访问网页。

  4、Cookie

Tornado中可以对cookie进行操作,并且还可以对cookie进行签名以放置伪造。

a、基本操作

#!/usr/bin/env python
# -*- coding:utf-8 -*-
   
import tornado.ioloop
import tornado.web
   
   
class MainHandler(tornado.web.RequestHandler):
    def get(self):
        print(self.cookies)              # 获取所有的cookie
        self.set_cookie('k1','v1')       # 设置cookie
        print(self.get_cookie('k1'))     # 获取指定的cookie
        self.write("Hello, world")
   
application = tornado.web.Application([
    (r"/index", MainHandler),
])
   
   
if __name__ == "__main__":
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()

b、签名

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

#!/usr/bin/env python
# -*- coding:utf-8 -*-
   
import tornado.ioloop
import tornado.web
    
class MainHandler(tornado.web.RequestHandler):
    def get(self):
         if not self.get_secure_cookie("mycookie"):             # 获取带签名的cookie
             self.set_secure_cookie("mycookie", "myvalue")      # 设置带签名的cookie
             self.write("Your cookie was not set yet!")
         else:
             self.write("Your cookie was set!")
application = tornado.web.Application([
    (r"/index", MainHandler),
])
   
if __name__ == "__main__":
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()

签名Cookie的本质是:

写cookie过程:

  • 将值进行base64加密

  • 对除值以外的内容进行签名,哈希算法(无法逆向解析)

  • 拼接 签名 + 加密值

读cookie过程:

  • 读取 签名 + 加密值

  • 对签名进行验证

  • base64解密,获取值内容

c、自定义session

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

#!/usr/bin/env python
# -*- coding:utf-8 -*-
 
import tornado.ioloop
import tornado.web
 
class BaseHandler(tornado.web.RequestHandler):
 
    def get_current_user(self):
        return self.get_secure_cookie("login_user")
 
class MainHandler(BaseHandler):
 
    @tornado.web.authenticated
    def get(self):
        login_user = self.current_user
        self.write(login_user)
 
class LoginHandler(tornado.web.RequestHandler):
    def get(self):
        self.current_user()
 
        self.render('login.html', **{'status': ''})
 
    def post(self, *args, **kwargs):
 
        username = self.get_argument('name')
        password = self.get_argument('pwd')
        if username == 'alex' and password == '123':
            self.set_secure_cookie('login_user', 'alex')
            self.redirect('/')
        else:
            self.render('login.html', **{'status': '用户名或密码错误'})
 
settings = {
    'template_path': 'template',
    'static_path': 'static',
    'static_url_prefix': '/static/',
    'cookie_secret': 'helldjkaskak',  # 设定加一段字符串的cookie
}
 
application = tornado.web.Application([
    (r"/index", MainHandler),
    (r"/login", LoginHandler),
], **settings)
 
 
if __name__ == "__main__":
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()
自定义登录验证

d、JavaScript操作Cookie

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

/*
设置cookie,指定秒数过期,
name表示传入的key,
value表示传入相对应的value值,
expires表示当前日期在加5秒过期
 */

function setCookie(name,value,expires){
    var temp = [];
    var current_date = new Date();
    current_date.setSeconds(current_date.getSeconds() + 5);
    document.cookie = name + "= "+ value +";expires=" + current_date.toUTCString();
}

  

  5、Session

 除请求对象之外,还有一个 session 对象。它允许你在不同请求间存储特定用户的信息。它是在 Cookies 的基础上实现的,并且对 Cookies 进行密钥签名要使用会话,你需要设置一个密钥。

操作session

  • 获取session:request.session[key]
  • 设置session:reqeust.session[key] = value
  • 删除session:del request[key]

区别:cookie机制采用的是在客户端保持状态的方案,而session机制采用的是在服务器端保持状态的方案

#!/usr/bin/env python
# -*- coding:utf-8 -*-

import tornado.ioloop
import tornado.web
from hashlib import sha1
import os, time

session_container = {}

create_session_id = lambda: sha1('%s%s' % (os.urandom(16), time.time())).hexdigest()


class Session(object):

    session_id = "__sessionId__"

    def __init__(self, request):
        #当你请求过来的时候,我先去get_cookie看看有没有cookie!目的是看看有没有Cookie如果有的话就不生成了,没有就生成!
        session_value = request.get_cookie(Session.session_id)
        #如果没有Cookie生成Cookie[创建随机字符串]
        if not session_value:
            self._id = create_session_id()
        else:
            #如果有直接将客户端的随机字符串设置给_id这个字段,随机字符串封装到self._id里了
            self._id = session_value
        #在给它设置一下
        request.set_cookie(Session.session_id, self._id)

    def __getitem__(self, key):
        ret = None
        try:
            ret =  session_container[self._id][key]
        except Exception,e:
            pass
        return ret


    def __setitem__(self, key, value):
        #判断是否有这个随机字符串
        if session_container.has_key(self._id):
            session_container[self._id][key] = value
        else:
            #如果没有就生成一个字典
            '''
            类似:随机字符串:{'IS_LOGIN':'True'}
            '''
            session_container[self._id] = {key: value}

    def __delitem__(self, key):
        del session_container[self._id][key]


class BaseHandler(tornado.web.RequestHandler):

    def initialize(self):
        '''
        这里initialize的self是谁?
        obj = LoginHandler()
        obj.initialize() ==>这里LoginHandler这个类里没有initialize这个方法,在他父类里有
        所以initialize得self就是LoginHandler的对象
        '''
        self.my_session = Session(self) #执行Session的构造方法并且把LoginHandler的对象传过去
        '''
        这个self.my_session = Session()
        看这个例子:
        self.xxx = '123'  在这里创建一个对象,在LoginHandler中是否可以通过self.xxx去访问123这个值?
        可以,因为self.xxx 是在get之前执行的,他们俩的对象都是LoginHandler对象
        '''

class MainHandler(BaseHandler):

    def get(self):
        ret = self.my_session['is_login']
        if ret:
            self.write('index')
        else:
            self.redirect("/login")

class LoginHandler(BaseHandler):
    def get(self):
        '''
        当用户访登录的时候我们就得给他写cookie了,但是这里没有写在哪里写了呢?
        在哪里呢?之前写的Handler都是继承的RequestHandler,这次继承的是BaseHandler是自己写的Handler
        继承自己的类,在类了加扩展initialize! 在这里我们可以在这里做获取用户cookie或者写cookie都可以在这里做
        '''
        '''
        我们知道LoginHandler对象就是self,我们可不可以self.set_cookie()可不可以self.get_cookie()
        '''
        # self.set_cookie()
        # self.get_cookie()

        self.render('login.html', **{'status': ''})

    def post(self, *args, **kwargs):
        #获取用户提交的用户名和密码
        username = self.get_argument('username')
        password = self.get_argument('pwd')
        if username == 'wupeiqi' and password == '123':
            #如果认证通过之后就可以访问这个self.my_session对象了!然后我就就可以吧Cookie写入到字典中了,NICE
            self.my_session['is_login'] = 'true'

            '''
            这里用到知识点是类里的:
            class Foo(object):
                def __getitem__(self,key):
                    print '__getitem__',key

                def __setitem__(self,key,value):
                    print '__setitem__',key,value

                def __delitem__(self,key):
                    print '__delitem__',key

            obj = Foo()
            result = obj['k1'] #自动触发执行  __getitem__
            obj['k2'] = 'wupeiqi' #自动触发执行 __setitem__
            del obj['k1'] #自动触发执行  __delitme__

            '''

            self.redirect('/index')
        else:
            self.render('login.html', **{'status': '用户名或密码错误'})



settings = {
    'template_path': 'template',
    'static_path': 'static',
    'static_url_prefix': '/static/',
    'cookie_secret': 'aiuasdhflashjdfoiuashdfiuh',
    'login_url': '/login'
}

application = tornado.web.Application([
    #创建两个URL 分别对应  MainHandler  LoginHandler
    (r"/index", MainHandler),
    (r"/login", LoginHandler),
], **settings)


if __name__ == "__main__":
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()
View Code
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import tornado.web
import tornado.ioloop

container = {}
class Session:
    def __init__(self, handler):
        self.handler = handler
        self.random_str = None

    def __genarate_random_str(self):
        import hashlib
        import time
        obj = hashlib.md5()
        obj.update(bytes(str(time.time()), encoding='utf-8'))
        random_str = obj.hexdigest()
        return random_str

    def __setitem__(self, key, value):
        # 在container中加入随机字符串
        # 定义专属于自己的数据
        # 在客户端中写入随机字符串
        # 判断,请求的用户是否已有随机字符串
        if not self.random_str:
            random_str = self.handler.get_cookie('__session__')
            if not random_str:
                random_str = self.__genarate_random_str()
                container[random_str] = {}
            else:
                # 客户端有随机字符串
                if random_str in container.keys():
                    pass
                else:
                    random_str = self.__genarate_random_str()
                    container[random_str] = {}
            self.random_str = random_str # self.random_str = asdfasdfasdfasdf

        container[self.random_str][key] = value
        self.handler.set_cookie("__session__", self.random_str)

    def __getitem__(self, key):
        # 获取客户端的随机字符串
        # 从container中获取专属于我的数据
        #  专属信息【key】
        random_str =  self.handler.get_cookie("__session__")
        if not random_str:
            return None
        # 客户端有随机字符串
        user_info_dict = container.get(random_str,None)
        if not user_info_dict:
            return None
        value = user_info_dict.get(key, None)
        return value


class BaseHandler(tornado.web.RequestHandler):
    def initialize(self):
        self.session = Session(self)
自定义session

  6、上传文件

6.1、Form表单上传

<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
    <input type="file" id="img" />
    <input type="button" onclick="UploadFile();" />
    <script src="/statics/jquery-1.12.4.js">
        function UploadFile(){
            var fileObj = $("#img")[0].files[0];
            var form = new FormData();
            form.append("user", "v1");
            form.append("favor", "1");
            form.append("fafafa", "fileObj");

            $.ajax({
                type:'POST',
                url:'/index',
                data:form,
                 processData: false,  // tell jQuery not to process the data
                contentType: false,  // tell jQuery not to set contentType
                success: function(arg) {
                    console.log(arg);
                }
            })
        }
    </script>

</body>
</html>
index
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import tornado.ioloop
import tornado.web

IMG_LIST = []
class IndexHandler(tornado.web.RequestHandler):
    def get(self):
        self.render('index.html', img_list=IMG_LIST)
    def post(self, *args, **kwargs):
        print(self.get_argument('user'))
        print(self.get_arguments('favor'))
        file_metas = self.request.files["fafafa"]
        # print(file_metas)
        for meta in file_metas:
            # 要上传的文件名
            file_name = meta['filename']
            import os
            with open(os.path.join('statics', 'img', file_name), 'wb') as up:
                up.write(meta['body'])
            IMG_LIST.append(file_name)
        self.write('{"status": 1, "message": "mmmm"}')

settings = {
    'template_path': 'views',
    'static_path': 'statics',
    'static_url_prefix': '/statics/',
}

application = tornado.web.Application([
    (r"/index", IndexHandler),
], **settings)


if __name__ == "__main__":
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()
start

源码文件夹:http://pan.baidu.com/s/1slL6gFV

6.2、AJAX上传

  xhr上传
  jquery上传
  基于iframe上传

源码文件夹:http://pan.baidu.com/s/1slL6gFV

  7、验证码

验证码原理在于后台自动创建一张带有随机内容的图片,然后将内容通过img标签输出到页面。

下面也有具体的验证代码,可仿照。

源码文件夹:http://pan.baidu.com/s/1slL6gFV

自定义Web组件

  1、面向对象基础

面向对象中通过索引的方式访问对象,需要内部实现 __getitem__ 、__delitem__、__setitem__方法

#!/usr/bin/env python
# -*- coding:utf-8 -*-
   
class Foo(object):
   
    def __getitem__(self, key):
        print  '__getitem__',key
   
    def __setitem__(self, key, value):
        print '__setitem__',key,value
   
    def __delitem__(self, key):
        print '__delitem__',key
   
   
   
obj = Foo()
result = obj['k1']
#obj['k2'] = 'wupeiqi'
#del obj['k1']

  2、Tornado扩展

Tornado框架中,默认执行Handler的get/post等方法之前默认会执行 initialize方法,所以可以通过自定义的方式使得所有请求在处理前执行操作

class BaseHandler(tornado.web.RequestHandler):
   
    def initialize(self):
        self.xxoo = "wupeiqi"
   
   
class MainHandler(BaseHandler):
   
    def get(self):
        print(self.xxoo)
        self.write('index')
 
class IndexHandler(BaseHandler):
   
    def get(self):
        print(self.xxoo)
        self.write('index')

  

  3、session

session其实就是定义在服务器端用于保存用户回话的容器,其必须依赖cookie才能实现。

#!/usr/bin/env python
# -*- coding:utf-8 -*-
  
import tornado.ioloop
import tornado.web
from hashlib import sha1
import os, time
  
session_container = {}
  
create_session_id = lambda: sha1('%s%s' % (os.urandom(16), time.time())).hexdigest()
  
  
class Session(object):
  
    session_id = "__sessionId__"
  
    def __init__(self, request):
        session_value = request.get_cookie(Session.session_id)
        if not session_value:
            self._id = create_session_id()
        else:
            self._id = session_value
        request.set_cookie(Session.session_id, self._id)
  
    def __getitem__(self, key):
        return session_container[self._id][key]
  
    def __setitem__(self, key, value):
        if session_container.has_key(self._id):
            session_container[self._id][key] = value
        else:
            session_container[self._id] = {key: value}
  
    def __delitem__(self, key):
        del session_container[self._id][key]
  
  
class BaseHandler(tornado.web.RequestHandler):
  
    def initialize(self):
        # my_session['k1']访问 __getitem__ 方法
        self.my_session = Session(self)
  
  
class MainHandler(BaseHandler):
  
    def get(self):
        print self.my_session['c_user']
        print self.my_session['c_card']
        self.write('index')
  
class LoginHandler(BaseHandler):
  
    def get(self):
        self.render('login.html', **{'status': ''})
  
    def post(self, *args, **kwargs):
  
        username = self.get_argument('name')
        password = self.get_argument('pwd')
        if username == 'wupeiqi' and password == '123':
  
            self.my_session['c_user'] = 'wupeiqi'
            self.my_session['c_card'] = '12312312309823012'
  
            self.redirect('/index')
        else:
            self.render('login.html', **{'status': '用户名或密码错误'})
  
settings = {
    'template_path': 'template',
    'static_path': 'static',
    'static_url_prefix': '/static/',
    'cookie_secret': 'aiuasdhflashjdfoiuashdfiuh',
    'login_url': '/login'
}
  
application = tornado.web.Application([
    (r"/index", MainHandler),
    (r"/login", LoginHandler),
], **settings)
  
  
if __name__ == "__main__":
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()

自定义Session

  

  4、表单验证

在Web程序中往往包含大量的表单验证的工作,如:判断输入是否为空,是否符合规则。

<!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>
    <form action="/index" method="post">

        <p>hostname: <input type="text" name="host" /> </p>
        <p>ip: <input type="text" name="ip" /> </p>
        <p>port: <input type="text" name="port" /> </p>
        <p>phone: <input type="text" name="phone" /> </p>
        <input type="submit" />
    </form>
</body>
</html>
HTML
#!/usr/bin/env python
# -*- coding:utf-8 -*-
  
import tornado.ioloop
import tornado.web
from hashlib import sha1
import os, time
import re
  
  
class MainForm(object):
    def __init__(self):
        self.host = "(.*)"
        self.ip = "^(25[0-5]|2[0-4]d|[0-1]?d?d)(.(25[0-5]|2[0-4]d|[0-1]?d?d)){3}$"
        self.port = '(d+)'
        self.phone = '^1[3|4|5|8][0-9]d{8}$'
  
    def check_valid(self, request):
        form_dict = self.__dict__
        for key, regular in form_dict.items():
            post_value = request.get_argument(key)
            # 让提交的数据 和 定义的正则表达式进行匹配
            ret = re.match(regular, post_value)
            print key,ret,post_value
  
  
class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.render('index.html')
    def post(self, *args, **kwargs):
        obj = MainForm()
        result = obj.check_valid(self)
        self.write('ok')
  
  
  
settings = {
    'template_path': 'template',
    'static_path': 'static',
    'static_url_prefix': '/static/',
    'cookie_secret': 'aiuasdhflashjdfoiuashdfiuh',
    'login_url': '/login'
}
  
application = tornado.web.Application([
    (r"/index", MainHandler),
], **settings)
  
  
if __name__ == "__main__":
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()

Python
python

 由于验证规则可以代码重用,所以可以如此定义:

#!/usr/bin/env python
# -*- coding:utf-8 -*-

import tornado.ioloop
import tornado.web
import re


class Field(object):

    def __init__(self, error_msg_dict, required):
        self.id_valid = False
        self.value = None
        self.error = None
        self.name = None
        self.error_msg = error_msg_dict
        self.required = required

    def match(self, name, value):
        self.name = name

        if not self.required:
            self.id_valid = True
            self.value = value
        else:
            if not value:
                if self.error_msg.get('required', None):
                    self.error = self.error_msg['required']
                else:
                    self.error = "%s is required" % name
            else:
                ret = re.match(self.REGULAR, value)
                if ret:
                    self.id_valid = True
                    self.value = ret.group()
                else:
                    if self.error_msg.get('valid', None):
                        self.error = self.error_msg['valid']
                    else:
                        self.error = "%s is invalid" % name


class IPField(Field):
    REGULAR = "^(25[0-5]|2[0-4]d|[0-1]?d?d)(.(25[0-5]|2[0-4]d|[0-1]?d?d)){3}$"

    def __init__(self, error_msg_dict=None, required=True):

        error_msg = {}  # {'required': 'IP不能为空', 'valid': 'IP格式错误'}
        if error_msg_dict:
            error_msg.update(error_msg_dict)

        super(IPField, self).__init__(error_msg_dict=error_msg, required=required)


class IntegerField(Field):
    REGULAR = "^d+$"

    def __init__(self, error_msg_dict=None, required=True):
        error_msg = {'required': '数字不能为空', 'valid': '数字格式错误'}
        if error_msg_dict:
            error_msg.update(error_msg_dict)

        super(IntegerField, self).__init__(error_msg_dict=error_msg, required=required)


class CheckBoxField(Field):

    def __init__(self, error_msg_dict=None, required=True):
        error_msg = {}  # {'required': 'IP不能为空', 'valid': 'IP格式错误'}
        if error_msg_dict:
            error_msg.update(error_msg_dict)

        super(CheckBoxField, self).__init__(error_msg_dict=error_msg, required=required)

    def match(self, name, value):
        self.name = name

        if not self.required:
            self.id_valid = True
            self.value = value
        else:
            if not value:
                if self.error_msg.get('required', None):
                    self.error = self.error_msg['required']
                else:
                    self.error = "%s is required" % name
            else:
                if isinstance(name, list):
                    self.id_valid = True
                    self.value = value
                else:
                    if self.error_msg.get('valid', None):
                        self.error = self.error_msg['valid']
                    else:
                        self.error = "%s is invalid" % name


class FileField(Field):
    REGULAR = "^(w+.pdf)|(w+.mp3)|(w+.py)$"

    def __init__(self, error_msg_dict=None, required=True):
        error_msg = {}  # {'required': '数字不能为空', 'valid': '数字格式错误'}
        if error_msg_dict:
            error_msg.update(error_msg_dict)

        super(FileField, self).__init__(error_msg_dict=error_msg, required=required)

    def match(self, name, value):
        self.name = name
        self.value = []
        if not self.required:
            self.id_valid = True
            self.value = value
        else:
            if not value:
                if self.error_msg.get('required', None):
                    self.error = self.error_msg['required']
                else:
                    self.error = "%s is required" % name
            else:
                m = re.compile(self.REGULAR)
                if isinstance(value, list):
                    for file_name in value:
                        r = m.match(file_name)
                        if r:
                            self.value.append(r.group())
                            self.id_valid = True
                        else:
                            self.id_valid = False
                            if self.error_msg.get('valid', None):
                                self.error = self.error_msg['valid']
                            else:
                                self.error = "%s is invalid" % name
                            break
                else:
                    if self.error_msg.get('valid', None):
                        self.error = self.error_msg['valid']
                    else:
                        self.error = "%s is invalid" % name

    def save(self, request, upload_path=""):

        file_metas = request.files[self.name]
        for meta in file_metas:
            file_name = meta['filename']
            with open(file_name,'wb') as up:
                up.write(meta['body'])


class Form(object):

    def __init__(self):
        self.value_dict = {}
        self.error_dict = {}
        self.valid_status = True

    def validate(self, request, depth=10, pre_key=""):

        self.initialize()
        self.__valid(self, request, depth, pre_key)

    def initialize(self):
        pass

    def __valid(self, form_obj, request, depth, pre_key):
        """
        验证用户表单请求的数据
        :param form_obj: Form对象(Form派生类的对象)
        :param request: Http请求上下文(用于从请求中获取用户提交的值)
        :param depth: 对Form内容的深度的支持
        :param pre_key: Html中name属性值的前缀(多层Form时,内部递归时设置,无需理会)
        :return: 是否验证通过,True:验证成功;False:验证失败
        """

        depth -= 1
        if depth < 0:
            return None
        form_field_dict = form_obj.__dict__
        for key, field_obj in form_field_dict.items():
            print key,field_obj
            if isinstance(field_obj, Form) or isinstance(field_obj, Field):
                if isinstance(field_obj, Form):
                    # 获取以key开头的所有的值,以参数的形式传至
                    self.__valid(field_obj, request, depth, key)
                    continue
                if pre_key:
                    key = "%s.%s" % (pre_key, key)

                if isinstance(field_obj, CheckBoxField):
                    post_value = request.get_arguments(key, None)
                elif isinstance(field_obj, FileField):
                    post_value = []
                    file_list = request.request.files.get(key, None)
                    for file_item in file_list:
                        post_value.append(file_item['filename'])
                else:
                    post_value = request.get_argument(key, None)

                print post_value
                # 让提交的数据 和 定义的正则表达式进行匹配
                field_obj.match(key, post_value)
                if field_obj.id_valid:
                    self.value_dict[key] = field_obj.value
                else:
                    self.error_dict[key] = field_obj.error
                    self.valid_status = False


class ListForm(object):
    def __init__(self, form_type):
        self.form_type = form_type
        self.valid_status = True
        self.value_dict = {}
        self.error_dict = {}

    def validate(self, request):
        name_list = request.request.arguments.keys() + request.request.files.keys()
        index = 0
        flag = False
        while True:
            pre_key = "[%d]" % index
            for name in name_list:
                if name.startswith(pre_key):
                    flag = True
                    break
            if flag:
                form_obj = self.form_type()
                form_obj.validate(request, depth=10, pre_key="[%d]" % index)
                if form_obj.valid_status:
                    self.value_dict[index] = form_obj.value_dict
                else:
                    self.error_dict[index] = form_obj.error_dict
                    self.valid_status = False
            else:
                break

            index += 1
            flag = False


class MainForm(Form):

    def __init__(self):
        # self.ip = IPField(required=True)
        # self.port = IntegerField(required=True)
        # self.new_ip = IPField(required=True)
        # self.second = SecondForm()
        self.fff = FileField(required=True)
        super(MainForm, self).__init__()

#
# class SecondForm(Form):
#
#     def __init__(self):
#         self.ip = IPField(required=True)
#         self.new_ip = IPField(required=True)
#
#         super(SecondForm, self).__init__()


class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.render('index.html')
    def post(self, *args, **kwargs):
        # for i in  dir(self.request):
        #     print i
        # print self.request.arguments
        # print self.request.files
        # print self.request.query
        # name_list = self.request.arguments.keys() + self.request.files.keys()
        # print name_list

        # list_form = ListForm(MainForm)
        # list_form.validate(self)
        #
        # print list_form.valid_status
        # print list_form.value_dict
        # print list_form.error_dict

        # obj = MainForm()
        # obj.validate(self)
        #
        # print "验证结果:", obj.valid_status
        # print "符合验证结果:", obj.value_dict
        # print "错误信息:"
        # for key, item in obj.error_dict.items():
        #     print key,item
        # print self.get_arguments('favor'),type(self.get_arguments('favor'))
        # print self.get_argument('favor'),type(self.get_argument('favor'))
        # print type(self.get_argument('fff')),self.get_argument('fff')
        # print self.request.files
        # obj = MainForm()
        # obj.validate(self)
        # print obj.valid_status
        # print obj.value_dict
        # print obj.error_dict
        # print self.request,type(self.request)
        # obj.fff.save(self.request)
        # from tornado.httputil import HTTPServerRequest
        # name_list = self.request.arguments.keys() + self.request.files.keys()
        # print name_list
        # print self.request.files,type(self.request.files)
        # print len(self.request.files.get('fff'))
        
        # obj = MainForm()
        # obj.validate(self)
        # print obj.valid_status
        # print obj.value_dict
        # print obj.error_dict
        # obj.fff.save(self.request)
        self.write('ok')



settings = {
    'template_path': 'template',
    'static_path': 'static',
    'static_url_prefix': '/static/',
    'cookie_secret': 'aiuasdhflashjdfoiuashdfiuh',
    'login_url': '/login'
}

application = tornado.web.Application([
    (r"/index", MainHandler),
], **settings)


if __name__ == "__main__":
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()
View Code

  5、tornado生成随机验证码

用python生成随机验证码需要借鉴一个插件,和一个io模块,实现起来也非常容易,当然也需要借鉴session来判断验证码是否错误,

下面写一段用户登录验证带验证码的,再看下效果,插件必须和执行文件必须放在更目录下

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import tornado.web
import tornado.ioloop

container = {}
class Session:
    def __init__(self, handler):
        self.handler = handler
        self.random_str = None

    def __genarate_random_str(self):
        import hashlib
        import time
        obj = hashlib.md5()
        obj.update(bytes(str(time.time()), encoding='utf-8'))
        random_str = obj.hexdigest()
        return random_str

    def __setitem__(self, key, value):
        # 在container中加入随机字符串
        # 定义专属于自己的数据
        # 在客户端中写入随机字符串
        # 判断,请求的用户是否已有随机字符串
        if not self.random_str:
            random_str = self.handler.get_cookie('__session__')
            if not random_str:
                random_str = self.__genarate_random_str()
                container[random_str] = {}
            else:
                # 客户端有随机字符串
                if random_str in container.keys():
                    pass
                else:
                    random_str = self.__genarate_random_str()
                    container[random_str] = {}
            self.random_str = random_str # self.random_str = asdfasdfasdfasdf

        container[self.random_str][key] = value
        self.handler.set_cookie("__session__", self.random_str)

    def __getitem__(self, key):
        # 获取客户端的随机字符串
        # 从container中获取专属于我的数据
        #  专属信息【key】
        random_str =  self.handler.get_cookie("__session__")
        if not random_str:
            return None
        # 客户端有随机字符串
        user_info_dict = container.get(random_str,None)
        if not user_info_dict:
            return None
        value = user_info_dict.get(key, None)
        return value


class BaseHandler(tornado.web.RequestHandler):
    def initialize(self):
        self.session = Session(self)


class LoginHandler(BaseHandler):
    def get(self, *args, **kwargs):
        self.render('login.html' ,state = "")

    def post(self, *args, **kwargs):
        username = self.get_argument('username')
        password = self.get_argument('password')
        code =self.get_argument('code')
        check_code = self.session['CheckCode']
        if username =="zhangyanlin" and password == "123" and code.upper() == check_code.upper():
            self.write("登录成功")
        else:
            self.render('login.html',state = "验证码错误")

class CheckCodeHandler(BaseHandler):
    def get(self, *args, **kwargs):
        import io
        import check_code
        mstream = io.BytesIO()
        img,code = check_code.create_validate_code()
        img.save(mstream,"GIF")
        self.session['CheckCode'] =code
        self.write(mstream.getvalue())


settings = {
    'template_path':'views',
    'cookie_secret': "asdasd",
}

application = tornado.web.Application([
    (r'/login',LoginHandler),
    (r'/check_code',CheckCodeHandler)
],**settings)

if __name__ == '__main__':
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()
stater.py
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>验证码</title>
</head>
<body>
    <form action="/login" method="post">
        <p>用户名: <input type="text" name="username"> </p>
        <p>密码: <input type="password" name="password"> </p>
        <p>验证码: <input type="text" name="code"><img src="/check_code" onclick="ChangeCode();" id = "checkcode"></p>
        <input type="submit" value="submit"> <span>{{state}}</span>
    </form>
<script type="text/javascript">  //当点击图片的时候,会刷新图片,这一段代码就可以实现
    function ChangeCode() {
        var code = document.getElementById('checkcode');
        code.src += "?";
    }
</script>
</body>
</html>
login.py

效果如下:

上面实例的源码文件夹:http://pan.baidu.com/s/1slL6gFV

更多Tornado知识详见:http://demo.pythoner.com/itt2zh/ch1.html

http://www.cnblogs.com/wupeiqi/articles/5702910.html

http://www.cnblogs.com/luotianshuai/p/5386494.html

原文地址:https://www.cnblogs.com/kongqi816-boke/p/5699866.html