〖Python〗-- Tornado基礎

【Tornado基礎】

概述

  Tornado 是 FriendFeed 使用的可擴展的非阻塞式 web 服務器及其相關工具的開源版本。這個 Web 框架看起來有些像web.py 或者 Google 的 webapp,不過爲了能有效利用非阻塞式服務器環境,這個 Web 框架還包含了一些相關的有用工具 和優化。javascript

  Tornado 和如今的主流 Web 服務器框架(包括大多數 Python 的框架)有着明顯的區別:它是非阻塞式服務器,並且速度至關快。得利於其 非阻塞的方式和對 epoll 的運用,Tornado 每秒能夠處理數以千計的鏈接,這意味着對於實時 Web 服務來講,Tornado 是一個理想的 Web 框架。咱們開發這個 Web 服務器的主要目的就是爲了處理 FriendFeed 的實時功能 ——在 FriendFeed 的應用裏每個活動用戶都會保持着一個服務器鏈接。(關於如何擴容 服務器,以處理數以千計的客戶端的鏈接的問題,請參閱 C10K problem。)css

下載安裝:html

1
2
3
4
pip3 install tornado
  
源碼安裝
https: / / pypi.python.org / packages / source / t / tornado / tornado - 4.3 .tar.gz

框架使用

1、快速上手

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#!/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" )
    
application  =  tornado.web.Application([
     (r "/index" , MainHandler),
])
    
    
if  __name__  = =  "__main__" :
     application.listen( 8888 )
     tornado.ioloop.IOLoop.instance().start()

執行過程:java

  • 第一步:執行腳本,監聽 8888 端口
  • 第二步:瀏覽器客戶端訪問 /index  -->  http://127.0.0.1:8888/index
  • 第三步:服務器接受請求,並交由對應的類處理該請求
  • 第四步:類接受到請求以後,根據請求方式(post / get / delete ...)的不一樣調用並執行相應的方法
  • 第五步:方法返回值的字符串內容發送瀏覽器

2、路由系統

  路由系統其實就是 url 和 類 的對應關係,這裏不一樣於其餘框架,其餘不少框架均是 url 對應 函數,Tornado中每一個url對應的是一個類。python

  PS:路由元組中,第一個參數是url,第二個是要執行的類,第三個是字典,第四個參數是name:" "。jquery

#!/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 StoryHandler(tornado.web.RequestHandler):
    def get(self, story_id):
        self.write("You requested the story " + story_id)
   
class BuyHandler(tornado.web.RequestHandler):
    def get(self):
        self.write("buy.wupeiqi.com/index")
   
application = tornado.web.Application([
    (r"/index", MainHandler),
    (r"/story/([0-9]+)", StoryHandler),
])
   
application.add_handlers('buy.wupeiqi.com$', [
    (r'/index',BuyHandler),
])
   
if __name__ == "__main__":
    application.listen(80)
    tornado.ioloop.IOLoop.instance().start()
路由系統舉例

Tornado中原生支持二級域名的路由,如:git

3、模板引擎

  Tornao中的模板語言和django中相似,模板引擎將模板文件載入內存,而後將數據嵌入其中,最終獲取到一個完整的字符串,再將字符串返回給請求者。web

  Tornado 的模板支持「控制語句」和「表達語句」,控制語句是使用 {% 和 %} 包起來的 例如 {% if len(items) > 2 %}。表達語句是使用 {{ 和 }} 包起來的,例如 {{ items[0] }}ajax

控制語句和對應的 Python 語句的格式基本徹底相同。咱們支持 ifforwhile 和 try,這些語句邏輯結束的位置須要用 {% end %} 作標記。還經過 extends 和 block 語句實現了模板繼承。這些在 template 模塊 的代碼文檔中有着詳細的描述。算法

  注:在使用模板前須要在setting中設置模板路徑:"template_path" : "tpl"

 一、基本使用

#!/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", list_info = [11,22,33])
  
application = tornado.web.Application([
    (r"/index", MainHandler),
])
  
  
if __name__ == "__main__":
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()
app.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" />
</head>
<body>

    <div>
        <ul>
            {% for item in list_info %}
                <li>{{item}}</li>
            {% end %}
        </ul>
    </div>
    
    <script src="{{static_url("js/jquery-1.8.2.min.js")}}"></script>
    
</body>
</html>
index.html
在模板中默認提供了一些函數、字段、類以供模板使用:

escape: tornado.escape.xhtml_escape 的別名
xhtml_escape: tornado.escape.xhtml_escape 的別名
url_escape: tornado.escape.url_escape 的別名
json_encode: tornado.escape.json_encode 的別名
squeeze: tornado.escape.squeeze 的別名
linkify: tornado.escape.linkify 的別名
datetime: Python 的 datetime 模組
handler: 當前的 RequestHandler 對象
request: handler.request 的別名
current_user: handler.current_user 的別名
locale: handler.locale 的別名
_: handler.locale.translate 的別名
static_url: for handler.static_url 的別名
xsrf_form_html: handler.xsrf_form_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" />
    {% 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>1024</li>
        <li>42區</li>
    </ul>
</div>
header.html
<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
    <title>Test</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

四、自定義UIMethod以UIModule,自定義模版函數

 a、定義

# uimethods.py
 
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

b、註冊

#!/usr/bin/env python
# -*- coding:utf-8 -*-
#!/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()
註冊使用示例

c、使用

<!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>
應用頁面

4、靜態文件

  對於靜態文件,能夠配置靜態文件的目錄和前段使用時的前綴,而且Tornaodo還支持靜態文件緩存。

#!/usr/bin/env python
# -*- coding:utf-8 -*-
 
import tornado.ioloop
import tornado.web
 
 
class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.render('home/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(80)
    tornado.ioloop.IOLoop.instance().start()
app.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

注:靜態文件緩存的實現

import hashlib

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()
view code

5、cookie

  Tornado中能夠對cookie進行操做,而且還能夠對cookie進行簽名以防止僞造。

一、基本操做

from tornado.web import RequestHandler

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        if not self.get_cookie("mycookie"): # 取不到cookies
            self.set_cookie("mycookie", "myvalue") # 設置cookies 
            self.write("Your cookie was not set yet!")
        else:
            self.write("Your cookie was set!")

基本操做(定義Handler類,在其中完成操做)
View Code

二、加密cookie(簽名)

  Cookie 很容易被惡意的客戶端僞造。加入你想在 cookie 中保存當前登錄用戶的 id 之類的信息,你須要對 cookie 做簽名以防止僞造。Tornado 經過 set_secure_cookie 和 get_secure_cookie 方法直接支持了這種功能。 要使用這些方法,你須要在建立應用時提供一個密鑰,名字爲 cookie_secret。 你能夠把它做爲一個關鍵詞參數傳入應用的設置中:

1
2
3
4
5
6
7
8
9
10
11
class  MainHandler(tornado.web.RequestHandler):
     def  get( self ):
         if  not  self .get_secure_cookie( "mycookie" ):
             self .set_secure_cookie( "mycookie" "myvalue" )
             self .write( "Your cookie was not set yet!" )
         else :
             self .write( "Your cookie was set!" )
              
application  =  tornado.web.Application([
     (r "/" , MainHandler),
], cookie_secret = "61oETzKXQAGaYdkL5gEmGeJJFuYh7EQnp2XdTP1o/Vo=" )
def _create_signature_v1(secret, *parts):
    hash = hmac.new(utf8(secret), digestmod=hashlib.sha1)
    for part in parts:
        hash.update(utf8(part))
    return utf8(hash.hexdigest())

# 加密
def _create_signature_v2(secret, s):
    hash = hmac.new(utf8(secret), digestmod=hashlib.sha256)
    hash.update(utf8(s))
    return utf8(hash.hexdigest())

def create_signed_value(secret, name, value, version=None, clock=None,
                        key_version=None):
    if version is None:
        version = DEFAULT_SIGNED_VALUE_VERSION
    if clock is None:
        clock = time.time

    timestamp = utf8(str(int(clock())))
    value = base64.b64encode(utf8(value))
    if version == 1:
        signature = _create_signature_v1(secret, name, value, timestamp)
        value = b"|".join([value, timestamp, signature])
        return value
    elif version == 2:
        # The v2 format consists of a version number and a series of
        # length-prefixed fields "%d:%s", the last of which is a
        # signature, all separated by pipes.  All numbers are in
        # decimal format with no leading zeros.  The signature is an
        # HMAC-SHA256 of the whole string up to that point, including
        # the final pipe.
        #
        # The fields are:
        # - format version (i.e. 2; no length prefix)
        # - key version (integer, default is 0)
        # - timestamp (integer seconds since epoch)
        # - name (not encoded; assumed to be ~alphanumeric)
        # - value (base64-encoded)
        # - signature (hex-encoded; no length prefix)
        def format_field(s):
            return utf8("%d:" % len(s)) + utf8(s)
        to_sign = b"|".join([
            b"2",
            format_field(str(key_version or 0)),
            format_field(timestamp),
            format_field(name),
            format_field(value),
            b''])

        if isinstance(secret, dict):
            assert key_version is not None, 'Key version must be set when sign key dict is used'
            assert version >= 2, 'Version must be at least 2 for key version support'
            secret = secret[key_version]

        signature = _create_signature_v2(secret, to_sign)
        return to_sign + signature
    else:
        raise ValueError("Unsupported version %d" % version)

# 解密
def _decode_signed_value_v1(secret, name, value, max_age_days, clock):
    parts = utf8(value).split(b"|")
    if len(parts) != 3:
        return None
    signature = _create_signature_v1(secret, name, parts[0], parts[1])
    if not _time_independent_equals(parts[2], signature):
        gen_log.warning("Invalid cookie signature %r", value)
        return None
    timestamp = int(parts[1])
    if timestamp < clock() - max_age_days * 86400:
        gen_log.warning("Expired cookie %r", value)
        return None
    if timestamp > clock() + 31 * 86400:
        # _cookie_signature does not hash a delimiter between the
        # parts of the cookie, so an attacker could transfer trailing
        # digits from the payload to the timestamp without altering the
        # signature.  For backwards compatibility, sanity-check timestamp
        # here instead of modifying _cookie_signature.
        gen_log.warning("Cookie timestamp in future; possible tampering %r",
                        value)
        return None
    if parts[1].startswith(b"0"):
        gen_log.warning("Tampered cookie %r", value)
        return None
    try:
        return base64.b64decode(parts[0])
    except Exception:
        return None


def _decode_fields_v2(value):
    def _consume_field(s):
        length, _, rest = s.partition(b':')
        n = int(length)
        field_value = rest[:n]
        # In python 3, indexing bytes returns small integers; we must
        # use a slice to get a byte string as in python 2.
        if rest[n:n + 1] != b'|':
            raise ValueError("malformed v2 signed value field")
        rest = rest[n + 1:]
        return field_value, rest

    rest = value[2:]  # remove version number
    key_version, rest = _consume_field(rest)
    timestamp, rest = _consume_field(rest)
    name_field, rest = _consume_field(rest)
    value_field, passed_sig = _consume_field(rest)
    return int(key_version), timestamp, name_field, value_field, passed_sig


def _decode_signed_value_v2(secret, name, value, max_age_days, clock):
    try:
        key_version, timestamp, name_field, value_field, passed_sig = _decode_fields_v2(value)
    except ValueError:
        return None
    signed_string = value[:-len(passed_sig)]

    if isinstance(secret, dict):
        try:
            secret = secret[key_version]
        except KeyError:
            return None

    expected_sig = _create_signature_v2(secret, signed_string)
    if not _time_independent_equals(passed_sig, expected_sig):
        return None
    if name_field != utf8(name):
        return None
    timestamp = int(timestamp)
    if timestamp < clock() - max_age_days * 86400:
        # The signature has expired.
        return None
    try:
        return base64.b64decode(value_field)
    except Exception:
        return None


def get_signature_key_version(value):
    value = utf8(value)
    version = _get_version(value)
    if version < 2:
        return None
    try:
        key_version, _, _, _, _ = _decode_fields_v2(value)
    except ValueError:
        return None

    return key_version
內部算法

簽名Cookie的本質是:

1
2
3
4
5
6
7
8
9
10
11
寫cookie過程:
 
     將值進行base64加密
     對除值之外的內容進行簽名,哈希算法(沒法逆向解析)
     拼接 簽名 + 加密值
 
讀cookie過程:
 
     讀取 簽名 + 加密值
     對簽名進行驗證
     base64解密,獲取值內容

注:許多API驗證機制和安全cookie的實現機制相同。

基於Cookie實現用戶驗證-Demo
#!/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 == 'aaa' and password == '123':
            self.set_secure_cookie('login_user', 'aaa')
            self.redirect('/')
        else:
            self.render('login.html', **{'status': '用戶名或密碼錯誤'})
 
settings = {
    'template_path': 'template',
    'static_path': 'static',
    'static_url_prefix': '/static/',
    'cookie_secret': 'aiuasdhflashjdfoiuashdfiuh', # 簽名cookies
    '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()
基於簽名Cookie實現用戶驗證-Demo

三、JavaScript操做Cookie

  因爲Cookie保存在瀏覽器端,因此在瀏覽器端也可使用JavaScript來操做Cookie。

1
2
3
4
5
6
7
8
9
/ *
設置cookie,指定秒數過時
  * /
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();
}

對於參數:

  • domain   指定域名下的cookie
  • path       域名下指定url中的cookie
  • secure    https使用

6、CSRF

  Tornado中的跨站請求僞造和Django中的類似,跨站僞造請求(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使用時,本質上就是去獲取本地的cookie,攜帶cookie再來發送請求。

7、上傳文件

一、Form表單上傳

<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
    <title>上傳文件</title>
</head>
<body>
    <form id="my_form" name="form" action="/index" method="POST"  enctype="multipart/form-data" >
        <input name="fff" id="my_file"  type="file" />
        <input type="submit" value="提交"  />
    </form>
</body>
</html>
HTML
#!/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')

    def post(self, *args, **kwargs):
        file_metas = self.request.files["fff"]
        # print(file_metas)
        for meta in file_metas:
            file_name = meta['filename']
            with open(file_name,'wb') as up:
                up.write(meta['body'])

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

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


if __name__ == "__main__":
    application.listen(8000)
    tornado.ioloop.IOLoop.instance().start()
Tornado框架

二、AJAX上傳

<!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>
        function UploadFile(){
            var fileObj = document.getElementById("img").files[0];

            var form = new FormData();
            form.append("k1", "v1");
            form.append("fff", fileObj);

            var xhr = new XMLHttpRequest();
            xhr.open("post", '/index', true);
            xhr.send(form);
        }
    </script>
</body>
</html>
HTML - XMLHttpRequest
<!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>
        function UploadFile(){
            var fileObj = $("#img")[0].files[0];
            var form = new FormData();
            form.append("k1", "v1");
            form.append("fff", 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>
HTML - jQuery
HTML - iframe
#!/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')

    def post(self, *args, **kwargs):
        file_metas = self.request.files["fff"]
        # print(file_metas)
        for meta in file_metas:
            file_name = meta['filename']
            with open(file_name,'wb') as up:
                up.write(meta['body'])

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

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


if __name__ == "__main__":
    application.listen(8000)
    tornado.ioloop.IOLoop.instance().start()
Tornado框架
<script type="text/javascript">
 
    $(document).ready(function () {
 
        $("#formsubmit").click(function () {
 
            var iframe = $('<iframe name="postiframe" id="postiframe" style="display: none"></iframe>');
 
            $("body").append(iframe);
 
            var form = $('#theuploadform');
            form.attr("action", "/upload.aspx");
            form.attr("method", "post");
 
            form.attr("encoding", "multipart/form-data");
            form.attr("enctype", "multipart/form-data");
 
            form.attr("target", "postiframe");
            form.attr("file", $('#userfile').val());
            form.submit();
 
            $("#postiframe").load(function () {
                iframeContents = this.contentWindow.document.body.innerHTML;
                $("#textarea").html(iframeContents);
            });
 
            return false;
 
        });
 
    });
 
</script>
 
 
<form id="theuploadform">
    <input id="userfile" name="userfile" size="50" type="file" />
    <input id="formsubmit" type="submit" value="Send File" />
</form>
 
<div id="textarea">
</div>
擴展:基於iframe實現Ajax上傳示例
$('#upload_iframe').load(function(){
                    var iframeContents = this.contentWindow.document.body.innerText;
                    iframeContents = JSON.parse(iframeContents);
                   
                })
View Code
function bindChangeAvatar1() {
            $('#avatarImg').change(function () {
                var file_obj = $(this)[0].files[0];
                $('#prevViewImg')[0].src = window.URL.createObjectURL(file_obj)
            })
        }

        function bindChangeAvatar2() {
            $('#avatarImg').change(function () {
                var file_obj = $(this)[0].files[0];
                var reader = new FileReader();
                reader.readAsDataURL(file_obj);
                reader.onload = function (e) {
                    $('#previewImg')[0].src = this.result;
                };
            })
        }

        function bindChangeAvatar3() {
            $('#avatarImg').change(function () {
                var file_obj = $(this)[0].files[0];
                var form = new FormData();
                form.add('img_upload', file_obj);

                $.ajax({
                    url: '',
                    data: form,
                    processData: false,  // tell jQuery not to process the data
                    contentType: false,  // tell jQuery not to set contentType
                    success: function (arg) {

                    }
                })
            })
        }

        function bindChangeAvatar4() {
            $('#avatarImg').change(function () {
                $(this).parent().submit();

                $('#upload_iframe').load(function () {
                    var iframeContents = this.contentWindow.document.body.innerText;
                    iframeContents = JSON.parse(iframeContents);
                    if (iframeContents.status) {
                        $('#previewImg').attr('src', '/' + iframeContents.data);
                    }
                })

            })
        }
其餘

8、驗證碼

  驗證碼原理在於後臺自動建立一張帶有隨機內容的圖片,而後將內容經過img標籤輸出到頁面。

我的學習總結

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
Tornado學習 總結
a. 問題:Web框架中都有什麼?
     Django:
         socket: 無,wsgiref
         中間件: 有
       路由系統: 有
       視圖函數: 有
        ORM操做: 有
       模板引擎: 有
     simple_tag:  有
         cookie:  有
        session: 有
           csrf: 有
            xss: 有
           其餘:緩存,信號,Form,ModelForm,Admin
       
     tornado:
         socket: 有,有wsgiref
       路由系統: 有
       視圖函數: 有
        ORM操做: 無
       模板引擎: 有
     simple_tag:  有,uimethod,uimodule
         cookie:  有
        session: 無
           csrf: 有
            xss: 有
            其餘:無
     
     總結:路由系統,視圖函數,模板引擎,cookie,csrf,xss
     
b. Tornado安裝
     pip3 install tornado
     
c. 基本使用
         import  tornado.ioloop
         import  tornado.web
         from  tornado.web  import  RequestHandler
 
 
         class  IndexHandler(RequestHandler):
             def  get( self ):
                 self .write( "Hello, world" )
 
 
         application  =  tornado.web.Application([
             (r "/index" , IndexHandler),
         ])
 
         if  __name__  = =  "__main__" :
             application.listen( 8888 )
             tornado.ioloop.IOLoop.instance().start()
             
     支持經過域名進行匹配
     路由元組中,第一個參數是url,第二個是要執行的類,第三個是字典,第四個參數是name:""
 
     self .request 中封裝了全部的請求信息
     
也有csrf_token
 
配置文件中:
     "xsrf_cookies" True ,
模版語言中:
     {{ raw xsrf_form_html() }}  #把字符串類型的標籤,轉成標籤
     
關於cookies
 
明文:
     self .set_cookie( "name" , "value傳入cookies的值" ,expires = time.time() + 10 (失效時間)) #設置cookies
     name  =  self .get_cookie( "name" #獲取cookies
 
密文:寫入標籤
     
     配置文件中: "cookie_secret" "asdf" , #配置加密cookies的信息
     
     設置:
     self .set_secure_cookie( "nnn" ,user,expires = time.time() + 10 #設置帶標籤的cookies
     name  =  self .get_secure_cookie( "nnn" #獲取帶標籤的cookies
 
配置文件中,能夠指定跳轉的url。
     
裝飾器登陸受權認證  給須要登陸驗證的函數加上裝飾器:@authenticated
 
     @authenticated   self 中執行current_user,他本質是讓咱們進行自定製的函數。
     查看關鍵函數的源碼發現
     if  not  hasattr ( self "_current_user" ):
         self ._current_user  =  self .get_current_user()
     return  self ._current_user
     是去判斷沒有方法的話,去執行get_current_user()函數,而這個函數內什麼內容都沒寫,因此說咱們可使用它自定義 get_current_user 函數,獲取加密的cookies並返回。
     
     # 應用
     from  tornado.web  import  RequestHandler
     from  tornado.web  import  authenticated
 
     #多繼承,獲取cookies,以裝飾器的形式 實現登陸認證
     class  BaseRequestHandler( object ):
         def  get_current_user( self ):
             return  self .get_secure_cookie( "nnn" )
 
     #注意繼承順序的問題
     class  SeedHandler(BaseRequestHandler,RequestHandler):
         @authenticated
         def  get( self * args,  * * kwargs):
             seed_list  =  [
                 { "title" : "小麥" , "price" : 12 },
                 { "title" : "大麥" , "price" : 14 },
                 { "title" : "蕎麥" , "price" : 16 },
                 { "title" : "麥麩" , "price" : 2 },
             ]
             self .render( "seed.html" ,seed_list = seed_list)
 
自定義模版函數 UIMethod以UIModule
     
     UIMethod
     建立一個文件,編寫自定製的函數方法
     有個默認參數: self ,包含了當前調用的類的全部東西
     
     使用的話,須要在配置文件註冊 
     頁面 {{ 函數名() }} 執行調用。若是是HTML標籤類型 { %  raw 函數名()  % } 把字符串轉成標籤
     
     
     UIModule
     調用方法
     { %  module 類名(傳值)  % }
     
     在後臺返回一個css的列表,渲染成多個導入css文件的link標籤
     
     返回嵌入類型的css是在頁面的頭文件中加入 style 標籤,裏邊是咱們寫的屬性
     
     不只能返回內容還能定製css和js。
 
模版語言中:
     也支持母板繼承的方式。與Django框架中的方法徹底一致。
     
tornado框架 內部執行流程
 
     程序運行,會執行自定義類內的方法(GET,POST......),在類定義的時候,確定是走了__init__的方法。最後在這個__init__函數中最後執行了一個鉤子函數   self .initialize( * * kwargs)
     
     重點是這個函數內部 沒有定義任何方法。因此咱們能夠在咱們本身寫的方法中重寫這個函數[ self .initialize( * * kwargs)]。
     
     在get,post等函數以前,確定是先執行這個函數。
     
     在路由系統中,第三個參數是個字典,用於初始化傳值,傳遞到的位置就是 self .initialize( * * kwargs)這個函數。

Tornado框架

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
#!/usr/bin/env python
# _*_ coding:utf-8 _*_
 
import  tornado.ioloop
import  tornado.web
from  tornado.web  import  RequestHandler   # 請求處理
 
#路由對應執行的請求處理方法 (類,繼承Tornado的requesthander方法)
class  IndexHandler(RequestHandler):
     #對於get請求
     def  get( self ):
 
         #在請求操做中反向生成url
         # url1 = self.application.reverse_url('n1')
         # print(url1)
         # url2 = self.application.reverse_url('n2',666)
         # print(url2)
 
         #返回頁面內容,至關於Django中的HttpResponse
         self .write( "Hello, world" )
 
         #返回模版頁面,至關於Django中的render
         # self.render("*.html模版路徑","返回值")
 
         # 返回值參數位置是 **kwargs,因此傳值的時候能夠寫一個字典,也能夠寫成一個個的k=v的傳參;
         # 有一點須要注意,模版中是經過傳值的key去渲染結果的【{{ key }}】 若是須要傳參的話,不論是GET請求或是POST,都須要對應的寫上返回的參數信息,
         # GET返回爲空,POST返回是真正的信息,不然會因模版渲染是找不到對應的key值而報錯。
 
         #跳轉頁面,至關於Django中的redirect
         # self.redirect("url路由地址")
 
         #關於GET/POST取值
         # self.get_query_argument("key") #獲取get某個key的一個傳參
         # self.get_query_arguments("key") #獲取get某個key的多個傳參
         #
         # self.get_argument("key") #從get和POST請求中獲取key的傳參,會去兩個請求中查找,
         # self.get_arguments("key") #從get和POST請求中獲取key的多個傳參,會去兩個請求中查找
 
class  HomeHandler(RequestHandler):
     def  get( self , * args, * * kwargs):
         import  time
 
         #獲取設置的cookies
         # self.set_cookie("name","value傳入cookies的值",expires=time.time()+10(失效時間))#設置cookies
         # name = self.get_cookie("name") #獲取cookies
         #獲取加密cookies
         # self.set_secure_cookie("name","value設置的cookies值",expires="超時時間") #設置加密的cookies
         # self.get_secure_cookie("name") #獲取加密的cookies
 
         self .write( "Hello, world" )
 
#配置文件,字典類型
settings  =  {
     "template_path" : "模版文件名" ,
     "static_path" : "靜態文件目錄" ,
     "static_url_prefix" : "找靜態文件的前綴" ,
     "xsrf_cookies" True #啓用csrf_token  默認 模版頁面中引用方法 {% raw xsrf_form_html() %}
     "cookie_secret" "asdf" , #用於配置加密cookies的信息
     'login_url' '/login.html' #起始的url
     "ui_methods" : "導入的自定義模版函數文件的函數名" #自定製函數 函數類型
     "ui_modules" : "導入的自定義模版類文件的類名" , #自定製函數 類
}
#建立web項目
application  =  tornado.web.Application([
     #路由系統
     (r "/index" , IndexHandler,{}, "n1" ),
     (r "/home/(\d+)" , HomeHandler,{}, 'n2' ),
], * * settings)  #項目下第一個參數是路由系統,第二個爲配置信息
 
#也能夠額外添加路由,當訪問的時候會先在這裏查找,若是沒有再去上邊的路由中去找。
application.add_handlers( "域名網址" ,[
     #路由
])
 
 
#主函數執行
if  __name__  = =  "__main__" :
     #項目啓動,監聽8888端口
     application.listen( 8888 )
     #IO複用開始執行
     tornado.ioloop.IOLoop.instance().start()
 
#模版語言引用靜態文件的方法
# 一、href=直接路由指定
# 二、href="{{static_url('樣式文件名')}}" 在頁面查看會發現,url地址後文件有一串隨機字符串(md5值),是後臺經過md5生成。好處,防止瀏覽器有靜態文件的緩存
 
# 模版語言與Django同樣也支持母板繼承,繼承方式如出一轍。
相關文章
相關標籤/搜索