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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM