web框架 之 Tornado

初識 Tornado :javascript

tornado web server 是使用python編寫出來的一個輕量級、高可伸縮性和非阻塞IO的Web服務器軟件,其特色是採用epoll非阻塞IO,相應快速,可處理數千併發鏈接,特別適用於實時的Web服務。css

概述:html

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

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

下載安裝:python

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

快速上手:jquery

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

執行過程:git

  • 第一步:執行腳本,監聽 8888 端口
  • 第二步:瀏覽器客戶端訪問 /index  -->  http://127.0.0.1:8888/index
  • 第三步:服務器接受請求,並交由對應的類處理該請求
  • 第四步:類接受到請求以後,根據請求方式(post / get / delete ...)的不一樣調用並執行相應的方法
  • 第五步:方法返回值的字符串內容發送瀏覽器
import tornado.ioloop
import tornado.web
 
class MainHandler(tornado.web.RequestHandler):
    def get(self):
        # self.render("s1.html")  #render方法,表示會自動找到文件並打開,返回給你
                                  #render找的時候默認從當前的目錄下面去找,若是想讓其從別的地方找,咱們就能夠
                                  #爲其作一個settings配置(也能夠把絕對路徑寫上去),
        self.write("Hello, world")
settings={                        #若是想讓配置文件生效,須要在下面application後面加一個**settings
    "tempalte_path":"xxx",        #模板路徑的匹配,其中xxx爲放HTML的文件夾
    "static_path":"xxx"           #靜態文件的配置(靜態文件就是css和JavaScript),其中xxx爲存放靜態文件的文件夾
}
 
#路由映射(路由系統)
application = tornado.web.Application([
    (r"/index", MainHandler),    #檢測用戶的url是否匹配,若是匹配則,執行其後面類中的莫一種方法
                                 #同一個url以不一樣的方式訪問,執行不一樣的方法
],**settings)
 
 
if __name__ == "__main__":
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()
具體分析

2、路由系統 (applicationgithub

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

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

內部在執行的時候執行了兩個方法__init__方法和self.add_handlers(".*$", handlers)方法{源碼後期解析Tornado時補充}

這個add_handlers默認傳輸的".*$" 就是www,他內部生成的路由映射的時候至關於(二級域名的方式)下圖:

咱們能夠經過application.add_handlers,添加一個「shuaige.com」,他會生成一個相似下面的對應關係shuaige.*

若是匹配的是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 -*-
__author__ = 'luotianshuai'

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("shuaige.com",([    #二級路由
    (r"/index", Shuaige),
])
)


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

模板:

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

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

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

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

settings = {
    'template_path':'views',             #設置模板路徑,HTML文件放置views文件夾中
    'static_path':'static',              # 設置靜態模板路徑,css,JS,Jquery等靜態文件放置static文件夾中
    'static_url_prefix': '/sss/',        #導入時候須要加上/sss/,例如<script src="/sss/jquery-1.9.1.min.js"></script>  本身去找該js文件
    'cookie_secret': "asdasd",           #cookie生成祕鑰時候需提早生成隨機字符串,須要在這裏進行渲染
    'xsrf_cokkies':True,                 #容許CSRF使用
}
application = tornado.web.Application([
    (r'/index',IndexHandler),       #  路由映射  路由系統
],**settings)                           
模板路徑
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        .pg-header{
            height: 48px;
            background-color: darkcyan;
        }
        .pg-footer{
            height: 100px;
            background-color:beige;
        }
    </style>
</head>
<body>
    <div class="pg-header">

    </div>
    <div class="pg-contet">
        {% block body %} {% end %}
    </div>
    <div class="pg-footer">AAAAAAAAAA</div>
    <script src="{{static_url('js/jquery-1.8.2.min.js')}}"></script>
    {% block js %} {% end %}
</body>
</html>
母版 layout.html
{% extends '../master/layout.html'%}

{% block body %}
    <h1>Index</h1>
    {% include  "../include/form.html" %}
{% end %}

{% block js %}

{% end %}
子模板 index.html

 模板中for循環的語法

{% extends '../master/layout.html'%}

{% block body %}
    <h1>Fuck</h1>
    {% include "../include/form.html" %}
    {% include "../include/form.html" %}
    {% include "../include/form.html" %}
    {% include "../include/form.html" %}
{% end %}
fuck.html
<form action="/">
    <input type="text"/>
    <input type="submit"/>
</form>
<ul>
    {% for item in list_info %}
        <li>{{item}}</li>
    {% end %}
</ul>index
form.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 lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="sss/commons.css" >
</head>
<body>
    <h1>提交數據</h1>
    <form method="get" action="/index">
        <input type="text" name="xxx" />
        <input type="submit" value="提交" />
    </form>
    <h1>展現內容</h1>
    <h3>{{npm}}</h3>
    <h3>{{ func(npm) }}</h3>
     {% module custom(123) %}
    <ul>
        {% for item in xxxooo %}
            {% if item == "alex" %}
                <li style="color: red">{{item}}</li>
            {% else %}
                <li>{{item}}</li>
            {% end %}
        {% end %}
    </ul>

    <script src="/sss/oldboy.js"></script>
</body>
</html>
index.html
#!/usr/bin/env python
#-*- coding:utf-8 -*-
import tornado.ioloop
import tornado.web
import uimethod as mt
import uimodule as md

INPUTS_LIST = ['alex']
class MainHandler(tornado.web.RequestHandler):
    def get(self):
        name = self.get_argument("xxx",None)
        if name:
            INPUTS_LIST.append(name)
        self.render('s1.html',npm="NPM888",xxxooo = INPUTS_LIST)
    # def post(self,*args,**kwargs):
    #     name = self.get_argument("xxx",None)
    #     INPUTS_LIST.append(name)
    #     self.render('s1.html',npm="NPM888",xxxooo = INPUTS_LIST)

settings={
    'template_path':'tpl',
    'static_path':'static',
    'static_url_prefix':'/sss/',
    'ui_methods':mt,
    'ui_modules':md
}

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

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

模板語言有三類:

一、{{npm}}-------------self.render("s1.html",npm = "NPM888")

二、代碼塊的方式

{% for item in xxxooo %}     ----   self.render('s1.html',xxxooo = INPUTS_LIST)
    {% if item == "alex" %}
<li style="color: red">{{item}}</li>
{% else %}
<li>{{item}}</li>
{% end %}
{% end %}

三、自定義

def func(self,arg):
    return "12345"
uimethods
#!/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>哈哈哈</h1>')
uimodule

cookie 

Cookie是當你瀏覽某網站時,網站存儲在你機器上的一個小文本文件,它記錄了你的用戶ID,密碼、瀏覽過的網頁、停留的時間等信息,當你再次來到該網站時,網站經過讀取Cookie,得知你的相關信息,就能夠作出相應的動做,如在頁面顯示歡迎你的標語,或者讓你不用輸入ID、密碼就直接登陸等。

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

import tornado.web

class IndexHandler(tornado.web.RequestHandler):
    def get(self):
        print(self.cookies)       #獲取http請求中攜帶的瀏覽器中的全部cookie
        print(self.get_cookie("k1"))  # 獲取瀏覽器中的cooki
        self.set_cookie('k1','111')     #爲瀏覽器設置cookie
        self.render('index.html')

settings = {
    'template_path':'views',
    'static_path':'static'
}
application = tornado.web.Application([
    (r'/index',IndexHandler),

],**settings)

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

<script>
    function setCookie(name,value,expires) {
        var current_date = new Date();
        current_date.setSeconds(current_date.getSeconds()+5);
        document.cookie = name + '='+ value +';expires='+ current_date.toUTCString();
    }
</script>

</body>
</html>
index.html

二、加密cookie(簽名)

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

簽名Cookie的本質是:

寫cookie過程:

  • 將值進行base64加密
  • 對除值之外的內容進行簽名,哈希算法(沒法逆向解析)
  • 拼接 簽名 + 加密值

v1 = base64(v1)

k1 =  v1 | 加密串(md5(v1+時間戳+自定義字符串)) | 時間戳

讀cookie過程:

  • 讀取 簽名 + 加密值
  • 對簽名進行驗證
  • base64解密,獲取值內容

 

#!/usr/bin/env/python
# -*- coding:utf-8 -*-
import tornado.web
import hashlib
import time
xin = {}     #建立一個空字典
class IndexHandler(tornado.web.RequestHandler):
    def get(self):
        if self.get_argument('u',None) in['kai','xin']:
            obj = hashlib.md5()
            obj.update(bytes(str(time.time()),encoding="utf-8"))
            random_str = obj.hexdigest()
            xin[random_str] = {}
            xin[random_str]['k1']=123
            xin[random_str]['k2']=self.get_argument('u',None)+'parents'
            xin[random_str]['is_login']=True
            self.set_cookie('iiii',random_str)
        else:
            self.write('請先登陸')

class ManagerHandler(tornado.web.RequestHandler):
    def get(self):
        random_str = self.get_cookie('iiii')
        xin_user_info = xin.get(random_str,None)
        if not xin_user_info:
            self.redirect('/index')
        else:
            if xin_user_info.get('is_login',None):
                time = "%s - %s " %(xin_user_info.get('k1',""),xin_user_info.get('k2',""))
                self.write(time)
            else:
                self.redirect('/index')

settings = {
    'template_path':'views',
    'static_path':'static'
}
application = tornado.web.Application([
    (r'/index',IndexHandler),
    (r'/manger',ManagerHandler),
],**settings)

if __name__ == "__main__":
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()
基於cookie 實現用戶驗證

5、Session(依賴於cookie)

因爲cookie中須要保存客戶的不少信息,並且若是信息不少的話,服務端與客戶端交互的時候也浪費流量,因此咱們須要用不多的一段字符串來保存不少的信息,這就是咱們所要引進的session。

cookie 和session 的區別:

一、cookie數據存放在客戶的瀏覽器上,session數據放在服務器上。

二、cookie不是很安全,別人能夠分析存放在本地的COOKIE並進行COOKIE欺騙    考慮到安全應當使用session。

三、session會在必定時間內保存在服務器上。當訪問增多,會比較佔用你服務器的性能    考慮到減輕服務器性能方面,應當使用COOKIE。

四、單個cookie保存的數據不能超過4K,不少瀏覽器都限制一個站點最多保存20個cookie。

五、因此我的建議:    將登錄信息等重要信息存放爲SESSION    其餘信息若是須要保留,能夠放在COOKIE中

 

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

container = {}
# container = {
#     # "第一我的的隨機字符串":{},
#     # "第一我的的隨機字符串":{'k1': 111, 'parents': '你'},
# }

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('__kakaka__')
            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("__kakaka__", self.random_str)

    def __getitem__(self, key):
        # 獲取客戶端的隨機字符串
        # 從container中獲取專屬於個人數據
        #  專屬信息【key】
        random_str =  self.handler.get_cookie("__kakaka__")
        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 IndexHandler(BaseHandler):
    def get(self):
        if self.get_argument('u',None) in ['alex','eric']:
            self.session['is_login'] = True
            self.session['name'] =self.get_argument('u',None)
            print(container)
        else:
            self.write('請你先登陸')
class MangerHandler(BaseHandler):
    def get(self):
        # s = Session(self)
        # val = s.get_value('is_login')
        val = self.session['is_login']
        if val:
            # self.write(s.get_value('name'))
            self.write(self.session['name'])
        else:
            self.write('登陸失敗')

class LoginHandler(BaseHandler):
    def get(self,*args,**kwargs):
        self.render('login.html',status="")
    def post(self, *args, **kwargs):
        user = self.get_argument('user',None)
        pwd = self.get_argument('pwd',None)
        code = self.get_argument('code',None)
        check_code = self.session['CheckCode']
        if code.upper() == check_code.upper():
            self.write('驗證碼正確')
        else:
            self.render('login.html',status ='驗證碼錯誤')

settings = {
    'template_path':'views',
    'statics_path':'static',
}
application = tornado.web.Application([
    (r'/index',IndexHandler),
    (r'/manger',MangerHandler),
    (r'/login',LoginHandler),
],**settings)

if __name__ == "__main__":
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()
利用session實現用戶驗證

 

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

settings = {
    'template_path': 'views',  # 模板路徑的配置
    'static_path': "static",  # 靜態文件
    "cookie_secrte": 'uiuoajskfjalsdjf',
}

# 路由映射,路由系統
application = tornado.web.Application([
    (r"/index/(?P<page>\d*)", home.IndexHandler),
], **settings)

application.add_handlers('buy.wupeiqi.com$',[
    (r"/index/(?P<page>\d*)", buy.IndexHandler),
])

if __name__ == "__main__":
    # socket運行起來
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()
實例 分頁 主模塊
#!/usr/bin/env python
# -*- coding:utf-8 -*-

class Pagenation:
    def __init__(self,current_page,all_item,base_url):
        try:
            page = int(current_page)
        except:
            page = 1
        if page < 1:
            page = 1

        all_pager, c = divmod(all_item, 5)
        if c > 0:
            all_pager += 1

        self.current_page = page
        self.all_pager = all_pager
        self.base_url = base_url

    @property
    def start(self):
        return (self.current_page - 1) * 5

    @property
    def end(self):
        return self.current_page * 5

    def string_pager(self):
        list_page = []
        if self.all_pager < 11:
            s = 1
            t = self.all_pager + 1
        else:  # 總頁數大於11
            if self.current_page < 6:
                s = 1
                t = 12
            else:
                if (self.current_page + 5) < self.all_pager:
                    s = self.current_page - 5
                    t = self.current_page + 5 + 1
                else:
                    s = self.all_pager - 11
                    t = self.all_pager + 1
        # 首頁
        first = '<a href="/index/1">首頁</a>'
        list_page.append(first)
        # 上一頁
        # 當前頁 page
        if self.current_page == 1:
            prev = '<a href="javascript:void(0);">上一頁</a>'
        else:
            prev = '<a href="/index/%s">上一頁</a>' % (self.current_page - 1,)
        list_page.append(prev)
        for p in range(s, t):  # 1-11
            if p == self.current_page:
                temp = '<a class="active" href="/index/%s">%s</a>' % (p, p)
            else:
                temp = '<a href="/index/%s">%s</a>' % (p, p)
            list_page.append(temp)
        if self.current_page == self.all_pager:
            nex = '<a href="javascript:void(0);">下一頁</a>'
        else:
            nex = '<a href="/index/%s">下一頁</a>' % (self.current_page + 1,)

        list_page.append(nex)

        # 尾頁
        last = '<a href="/index/%s">尾頁</a>' % (self.all_pager,)
        list_page.append(last)

        # 跳轉
        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>"""
        list_page.append(jump)
        list_page.append(script)
        str_page = "".join(list_page)
        return str_page
繼承子模塊
#!/usr/bin/env python
# -*- coding:utf-8 -*-

import tornado.web
from commons import pager
LIST_INFO = [
    {'username': 'alex', "email": "alex3721@163.com"},
]
for i in range(300):
    temp = {'username': 'alex'+str(i), "email": str(i) + '123@qq.com'}
    LIST_INFO.append(temp)

class IndexHandler(tornado.web.RequestHandler):

    def get(self, page):
        obj = pager.Pagenation(page, len(LIST_INFO), '/index/')
        current_list = LIST_INFO[obj.start:obj.end]
        str_page = obj.string_pager()
        self.render('home/index.html', list_info = current_list, current_page = obj.current_page, str_page = str_page)

    def post(self,page):
        user = self.get_argument('username')
        email = self.get_argument('email')
        temp = {'username': user, 'email': email}
        LIST_INFO.append(temp)
        self.redirect("/index/"+page)
子模塊
<!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: cadetblue;
        }
        .pager a.active{
            background-color: brown;
            color: white;
        }
    </style>
</head>
<body>
    <h1>提交數據</h1>
    <form method="post" action="/index/{{current_page}}">
        <input name="username" type="text" />
        <input name="email" type="text" />
        <input type="submit" value="提交" />
    </form>
    <h1>顯示數據</h1>
    <table border="1">
        <thead>
            <tr>
                <th>用戶名</th>
                <th>郵箱</th>
            </tr>
        </thead>
        <tbody>
            {% for line in list_info %}
                <tr>
                    <!--<td>{{line['username']}}</td>-->
                    <td>{{ line['username'] }}</td>
                    <td>{{line['email']}}</td>
                </tr>
            {% end %}
        </tbody>
    </table>
    <div class="pager">
        {% raw str_page %}
    </div>
</body>
</html>
index.html

tornado 隨機驗證碼

登錄註冊的時候,須要驗證碼的功能,原理爲在後臺自動建立一張隨機圖片,而後經過img標籤輸出到前端。這裏咱們須要安裝一個pillow的模塊,相應的生成隨機驗證代碼文件以下,此外還須要一個字體文件

安裝圖像處理模塊:

pip3 install pillow  
#!/usr/bin/env python
#coding:utf-8

import random
from PIL import Image, ImageDraw, ImageFont, ImageFilter

_letter_cases = "abcdefghjkmnpqrstuvwxy"  # 小寫字母,去除可能干擾的i,l,o,z
_upper_cases = _letter_cases.upper()  # 大寫字母
_numbers = ''.join(map(str, range(3, 10)))  # 數字
init_chars = ''.join((_letter_cases, _upper_cases, _numbers))

def create_validate_code(size=(120, 30),
                         chars=init_chars,
                         img_type="GIF",
                         mode="RGB",
                         bg_color=(255, 255, 255),
                         fg_color=(0, 0, 255),
                         font_size=18,
                         font_type="Monaco.ttf",
                         length=4,
                         draw_lines=True,
                         n_line=(1, 2),
                         draw_points=True,
                         point_chance = 2):
    '''
    @todo: 生成驗證碼圖片
    @param size: 圖片的大小,格式(寬,高),默認爲(120, 30)
    @param chars: 容許的字符集合,格式字符串
    @param img_type: 圖片保存的格式,默認爲GIF,可選的爲GIF,JPEG,TIFF,PNG
    @param mode: 圖片模式,默認爲RGB
    @param bg_color: 背景顏色,默認爲白色
    @param fg_color: 前景色,驗證碼字符顏色,默認爲藍色#0000FF
    @param font_size: 驗證碼字體大小
    @param font_type: 驗證碼字體,默認爲 ae_AlArabiya.ttf
    @param length: 驗證碼字符個數
    @param draw_lines: 是否劃干擾線
    @param n_lines: 干擾線的條數範圍,格式元組,默認爲(1, 2),只有draw_lines爲True時有效
    @param draw_points: 是否畫干擾點
    @param point_chance: 干擾點出現的機率,大小範圍[0, 100]
    @return: [0]: PIL Image實例
    @return: [1]: 驗證碼圖片中的字符串
    '''

    width, height = size # 寬, 高
    img = Image.new(mode, size, bg_color) # 建立圖形
    draw = ImageDraw.Draw(img) # 建立畫筆

    def get_chars():
        '''生成給定長度的字符串,返回列表格式'''
        return random.sample(chars, length)

    def create_lines():
        '''繪製干擾線'''
        line_num = random.randint(*n_line) # 干擾線條數

        for i in range(line_num):
            # 起始點
            begin = (random.randint(0, size[0]), random.randint(0, size[1]))
            #結束點
            end = (random.randint(0, size[0]), random.randint(0, size[1]))
            draw.line([begin, end], fill=(0, 0, 0))

    def create_points():
        '''繪製干擾點'''
        chance = min(100, max(0, int(point_chance))) # 大小限制在[0, 100]

        for w in range(width):
            for h in range(height):
                tmp = random.randint(0, 100)
                if tmp > 100 - chance:
                    draw.point((w, h), fill=(0, 0, 0))

    def create_strs():
        '''繪製驗證碼字符'''
        c_chars = get_chars()
        strs = ' %s ' % ' '.join(c_chars) # 每一個字符先後以空格隔開

        font = ImageFont.truetype(font_type, font_size)
        font_width, font_height = font.getsize(strs)

        draw.text(((width - font_width) / 3, (height - font_height) / 3),
                    strs, font=font, fill=fg_color)

        return ''.join(c_chars)

    if draw_lines:
        create_lines()
    if draw_points:
        create_points()
    strs = create_strs()

    # 圖形扭曲參數
    params = [1 - float(random.randint(1, 2)) / 100,
              0,
              0,
              0,
              1 - float(random.randint(1, 10)) / 100,
              float(random.randint(1, 2)) / 500,
              0.001,
              float(random.randint(1, 2)) / 500
              ]
    img = img.transform(size, Image.PERSPECTIVE, params) # 建立扭曲

    img = img.filter(ImageFilter.EDGE_ENHANCE_MORE) # 濾鏡,邊界增強(閾值更大)

    return img, strs
check_code.py 隨機驗證碼 模塊
#!/usr/bin/env/python
# -*- coding:utf-8 -*-
import tornado.web

container = {}
# container = {
#     # "第一我的的隨機字符串":{},
#     # "第一我的的隨機字符串":{'k1': 111, 'parents': '你'},
# }

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('__kakaka__')
            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("__kakaka__", self.random_str)

    def __getitem__(self, key):
        # 獲取客戶端的隨機字符串
        # 從container中獲取專屬於個人數據
        #  專屬信息【key】
        random_str =  self.handler.get_cookie("__kakaka__")
        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 IndexHandler(BaseHandler):
    def get(self):
        if self.get_argument('u',None) in ['alex','eric']:
            self.session['is_login'] = True
            self.session['name'] =self.get_argument('u',None)
            print(container)
        else:
            self.write('請你先登陸')
class MangerHandler(BaseHandler):
    def get(self):
        # s = Session(self)
        # val = s.get_value('is_login')
        val = self.session['is_login']
        if val:
            # self.write(s.get_value('name'))
            self.write(self.session['name'])
        else:
            self.write('登陸失敗')

class LoginHandler(BaseHandler):
    def get(self,*args,**kwargs):
        self.render('login.html',status="")
    def post(self, *args, **kwargs):
        user = self.get_argument('user',None)
        pwd = self.get_argument('pwd',None)
        code = self.get_argument('code',None)
        check_code = self.session['CheckCode']
        if code.upper() == check_code.upper():
            self.write('驗證碼正確')
        else:
            self.render('login.html',status ='驗證碼錯誤')
class CheckCodeHandler(BaseHandler):
    def get(self,*args,**kwargs):
        import io
        import check_code
        mstream = io.BytesIO()
        #建立圖片,並寫入驗證碼
        img, code = check_code.create_validate_code()
            #將圖片對象寫入到mstrem
        img.save(mstream,'GIF')

        self.session['CheckCode']=code
        self.write(mstream.getvalue())

class CsrfHandler(BaseHandler):
    def get(self,*args,**kwargs):
        self.render('csrf.html')
    def post(self, *args, **kwargs):
        self.write('csrf.post')

settings = {
    'template_path':'views',
    'statics_path':'static',
    'xsrf_cookies':True
}
application = tornado.web.Application([
    (r'/index',IndexHandler),
    (r'/manger',MangerHandler),
    (r'/login',LoginHandler),
    (r'/check_code',CheckCodeHandler),
    (r'/csrf',CsrfHandler)
],**settings)

if __name__ == "__main__":
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()
python 主模塊
<!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>
        <p>
            <input name="code" type="text" placeholder="驗證碼">
            <img src="/check_code" onclick="ChangeCode(); id = imgCode">
        </p>
        <input type="submit" value="提交"/>
        <span style="color: #ac2925"></span>
    </form>

 <script src="/static/jquery-1.12.4.js"></script>

    <script>
        function ChangeCode() {
            var code = document.getElementById('imgCode');
            code.sre += "?";
        }

    </script>
</body>
</html>
html

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)  #傳入兩個前端代碼的字符串
python
<!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>  
html

csrf跨站請求僞造
  get請求的時候,會給瀏覽器發一個id(cookie),瀏覽器post請求的時候,攜帶這個id,而後服務端對其作驗證,若是沒有這個id的話,就禁止瀏覽器提交內容。下面來看一下在tornado裏面怎麼設置,首先須要在settings裏面配置 'xsrf_cookies': True,若是這樣配置的話,瀏覽器發送post請求的話這樣設置以後,Tornado 將拒絕請求參數中不包含正確的_xsrf 值的 post/put/delete 請求,若是沒有攜帶相應的id(session)則會禁止訪問。{% raw xsrf_form_html() %}是新增的,目的就在於實現上面所說的受權給前端以合法請求。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form action="/csrf" method="post">
        {% raw xsrf_form_html() %}
        <input type="button" value="Ajax CSRF" onclick="SubmitCsrf();" />
    </form>

 <script src="/static/jquery-1.12.4.js"></script>

    <script>

        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.html
#!/usr/bin/env/python
# -*- coding:utf-8 -*-
import tornado.web

class CsrfHandler(BaseHandler):
    def get(self,*args,**kwargs):
        self.render('csrf.html')
    def post(self, *args, **kwargs):
        self.write('csrf.post')

settings = {
    'template_path':'views',
    'statics_path':'static',
    'xsrf_cookies':True
}
application = tornado.web.Application([
    (r'/csrf',CsrfHandler)
],**settings)

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

 ajax

爲何使用ajax,局部刷新,減小請求中發送的數據

AJAX,Asynchronous JavaScript and XML (異步的JavaScript和XML),一種建立交互式網頁應用的網頁開發技術方案。

  • 異步的JavaScript:
    使用 【JavaScript語言】 以及 相關【瀏覽器提供類庫】 的功能向服務端發送請求,當服務端處理完請求以後,【自動執行某個JavaScript的回調函數】。以上請求和響應的整個過程是【偷偷】進行的,頁面上無任何感知。
  • XML
    XML是一種標記語言,是Ajax在和後臺交互時傳輸數據的格式之一,可是如今使用的不多,基本都是使用json來作數據交換

利用AJAX能夠作:
一、註冊時,輸入用戶名自動檢測用戶是否已經存在。
二、登錄時,提示用戶名密碼錯誤
三、刪除數據行時,將行ID發送到後臺,後臺在數據庫中刪除,數據庫刪除成功後,在頁面DOM中將數據行也刪除。

首先來看一下一種用iframe標籤模擬ajax請求

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

import tornado.web

IMG_LIST = []

class PicturesHandler(tornado.web.RequestHandler):
    def get(self, *args, **kwargs):
        self.render('iframe.html',img_list = IMG_LIST)

    def post(self, *args, **kwargs):
        print(self.get_argument('user'))
        print(self.get_arguments('favor'))
        file_maetas = self.request.files['hahaha']
        for meta in file_maetas:
            file_name = meta['filename']
            import os
            with open(os.path.join('static','img',file_name),'wb')as up:
                up.write(meta["body"])
            IMG_LIST.append(file_name)
        self.write('{"status":1,"message":"mmm"}')



settings = {
    'template_path':"views",
    'static_path':'static',
}
application = tornado.web.Application([

    (r'/iframe',PicturesHandler)
],**settings)

if __name__ == "__main__":
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()
iframe模擬ajax app.py
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
            <div>
            <p>請輸入要加載的地址:<span id="currentTime"></span></p>
            <p>
                <input id="url" type="text" />
                <input type="button" value="刷新" onclick="LoadPage();">
            </p>
        </div>


        <div>
            <h3>加載頁面位置:</h3>
            <iframe id="iframePosition" style="width: 100%;height: 500px;"></iframe>
        </div>


        <script type="text/javascript">

            window.onload= function(){
                var myDate = new Date();
                document.getElementById('currentTime').innerText = myDate.getTime();

            };

            function LoadPage(){
                var targetUrl =  document.getElementById('url').value;
                document.getElementById("iframePosition").src = targetUrl;
            }

        </script>
</body>
</html>
iframe.html

Ajax主要就是使用 【XmlHttpRequest】對象來完成請求的操做,該對象在主流瀏覽器中均存在(除早起的IE),Ajax首次出現IE5.5中存在(ActiveX控件)

XmlHttpRequest對象介紹

XmlHttpRequest對象的主要方法:

a. void open(String method,String url,Boolen async)
   用於建立請求
   參數:
       method: 請求方式(字符串類型),如:POST、GET、DELETE...
       url:    要請求的地址(字符串類型)
       async:  是否異步(布爾類型)
  
b. void send(String body)
    用於發送請求
    參數:
        body: 要發送的數據(字符串類型)
 
c. void setRequestHeader(String header,String value)
    用於設置請求頭
    參數:
        header: 請求頭的key(字符串類型)
        vlaue:  請求頭的value(字符串類型)
 
d. String getAllResponseHeaders()
    獲取全部響應頭
    返回值:
        響應頭數據(字符串類型)
 
e. String getResponseHeader(String header)
    獲取響應頭中指定header的值
    參數:
        header: 響應頭的key(字符串類型)
    返回值:
        響應頭中指定的header對應的值
 
f. void abort()
    終止請求

XmlHttpRequest對象的主要屬性:

a. Number readyState
   狀態值(整數)
   詳細:
      0-未初始化,還沒有調用open()方法;
      1-啓動,調用了open()方法,未調用send()方法;
      2-發送,已經調用了send()方法,未接收到響應;
      3-接收,已經接收到部分響應數據;
      4-完成,已經接收到所有響應數據;
 
b. Function onreadystatechange
   當readyState的值改變時自動觸發執行其對應的函數(回調函數)
 
c. String responseText
   服務器返回的數據(字符串類型)
 
d. XmlDocument responseXML
   服務器返回的數據(Xml對象)
 
e. Number states
   狀態碼(整數),如:200、404...
 
f. String statesText
 
   狀態文本(字符串),如:OK、NotFound...

跨瀏覽器支持

  XmlHttpRequest IE7+, Firefox, Chrome, Opera, etc.

  • ActiveXObject("Microsoft.XMLHTTP") IE6, IE5
<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title></title>
</head>
<body>

    <h1>XMLHttpRequest - Ajax請求</h1>
    <input type="button" onclick="XmlGetRequest();" value="Get發送請求" />
    <input type="button" onclick="XmlPostRequest();" value="Post發送請求" />

    <script src="/statics/jquery-1.12.4.js"></script>
    <script type="text/javascript">

        function GetXHR(){
            var xhr = null;
            if(XMLHttpRequest){
                xhr = new XMLHttpRequest();
            }else{
                xhr = new ActiveXObject("Microsoft.XMLHTTP");
            }
            return xhr;

        }

        function XhrPostRequest(){
            var xhr = GetXHR();
            // 定義回調函數
            xhr.onreadystatechange = function(){
                if(xhr.readyState == 4){
                    // 已經接收到所有響應數據,執行如下操做
                    var data = xhr.responseText;
                    console.log(data);
                }
            };
            // 指定鏈接方式和地址----文件方式
            xhr.open('POST', "/test/", true);
            // 設置請求頭
            xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset-UTF-8');
            // 發送請求
            xhr.send('n1=1;n2=2;');
        }

        function XhrGetRequest(){
            var xhr = GetXHR();
            // 定義回調函數
            xhr.onreadystatechange = function(){
                if(xhr.readyState == 4){
                    // 已經接收到所有響應數據,執行如下操做
                    var data = xhr.responseText;
                    console.log(data);
                }
            };
            // 指定鏈接方式和地址----文件方式
            xhr.open('get', "/test/", true);
            // 發送請求
            xhr.send();
        }

    </script>

</body>
</html>
基於原生ajax

jQuery其實就是一個JavaScript的類庫,其將複雜的功能作了上層封裝,使得開發者能夠在其基礎上寫更少的代碼實現更多的功能。

  • jQuery 不是生產者,而是大天然搬運工。
  • jQuery Ajax本質 XMLHttpRequest 或 ActiveXObject 

注:2.+版本再也不支持IE9如下的瀏覽器

 jQuery.get(...)
                全部參數:
                     url: 待載入頁面的URL地址
                    data: 待發送 Key/value 參數。
                 success: 載入成功時回調函數。
                dataType: 返回內容格式,xml, json,  script, text, html


            jQuery.post(...)
                全部參數:
                     url: 待載入頁面的URL地址
                    data: 待發送 Key/value 參數
                 success: 載入成功時回調函數
                dataType: 返回內容格式,xml, json,  script, text, html


            jQuery.getJSON(...)
                全部參數:
                     url: 待載入頁面的URL地址
                    data: 待發送 Key/value 參數。
                 success: 載入成功時回調函數。


            jQuery.getScript(...)
                全部參數:
                     url: 待載入頁面的URL地址
                    data: 待發送 Key/value 參數。
                 success: 載入成功時回調函數。


            jQuery.ajax(...)

                部分參數:

                        url:請求地址
                       type:請求方式,GET、POST(1.9.0以後用method)
                    headers:請求頭
                       data:要發送的數據
                contentType:即將發送信息至服務器的內容編碼類型(默認: "application/x-www-form-urlencoded; charset=UTF-8")
                      async:是否異步
                    timeout:設置請求超時時間(毫秒)

                 beforeSend:發送請求前執行的函數(全局)
                   complete:完成以後執行的回調函數(全局)
                    success:成功以後執行的回調函數(全局)
                      error:失敗以後執行的回調函數(全局)
                

                    accepts:經過請求頭髮送給服務器,告訴服務器當前客戶端課接受的數據類型
                   dataType:將服務器端返回的數據轉換成指定類型
                                   "xml": 將服務器端返回的內容轉換成xml格式
                                  "text": 將服務器端返回的內容轉換成普通文本格式
                                  "html": 將服務器端返回的內容轉換成普通文本格式,在插入DOM中時,若是包含JavaScript標籤,則會嘗試去執行。
                                "script": 嘗試將返回值看成JavaScript去執行,而後再將服務器端返回的內容轉換成普通文本格式
                                  "json": 將服務器端返回的內容轉換成相應的JavaScript對象
                                 "jsonp": JSONP 格式
                                          使用 JSONP 形式調用函數時,如 "myurl?callback=?" jQuery 將自動替換 ? 爲正確的函數名,以執行回調函數

                                  若是不指定,jQuery 將自動根據HTTP包MIME信息返回相應類型(an XML MIME type will yield XML, in 1.4 JSON will yield a JavaScript object, in 1.4 script will execute the script, and anything else will be returned as a string

                 converters: 轉換器,將服務器端的內容根據指定的dataType轉換類型,並傳值給success回調函數
                         $.ajax({
                              accepts: {
                                mycustomtype: 'application/x-some-custom-type'
                              },
                              
                              // Expect a `mycustomtype` back from server
                              dataType: 'mycustomtype'

                              // Instructions for how to deserialize a `mycustomtype`
                              converters: {
                                'text mycustomtype': function(result) {
                                  // Do Stuff
                                  return newresult;
                                }
                              },
                            });
JQuery ajax 方法列表
<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
    <p>
        <input type="button" onclick="JqSendRequest();" value='Ajax請求' />
    </p>
    <script type="text/javascript" src="/c/static/jquery-1.9.1.min.js"></script>
    <script>
        function JqSendRequest(){
            $.ajax({
                url: "http://c2.com:8000/test/",
                type: 'GET',
                data:{"k1":"v1"},  //向服務端發送內容,服務端能夠經過self.get_argument("k1")獲取
                dataType: 'text',
                success: function(data, statusText, xmlHttpRequest){
                    console.log(data,statusText);
                }
            })
        }
    </script>
</body>
</html>
基於JQuery ajax

4、跨域AJAX

1.什麼引發了ajax跨域不能的問題
ajax自己其實是經過XMLHttpRequest對象來進行數據的交互,而瀏覽器出於安全考慮,不容許js代碼進行跨域操做,因此會警告。

特別的:因爲同源策略是瀏覽器的限制,因此請求的發送和響應是能夠進行,只不過瀏覽器不接受罷了。

瀏覽器同源策略並非對全部的請求均制約:

  • 制約: XmlHttpRequest
  • 不叼: img、iframe、script等具備src屬性的標籤

跨域,跨域名訪問,如:http://www.c1.com 域名向 http://www.c2.com域名發送請求。

一、JSONP實現跨域請求(利用script塊的特性)

JSONP(JSONP - JSON with Padding是JSON的一種「使用模式」),利用script標籤的src屬性(瀏覽器容許script標籤跨域)

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

import tornado.web

class IndexHandler(tornado.web.RequestHandler):
    def get(self):
        self.render('index.html')
    def post(self, *args, **kwargs):
        self.write('t1.post')

settings = {
    'template_path':'views',
    'static_path':'static',
}
application = tornado.web.Application([
    (r'/index',IndexHandler),
],**settings)

if __name__ == "__main__":
    application.listen(8001)
    tornado.ioloop.IOLoop.instance().start()
app1.py
#!/usr/bin/env/python
# -*- coding:utf-8 -*-
import tornado.web
class IndexHandler(tornado.web.RequestHandler):
    def get(self):
        callback = self.get_argument('callback')
        self.write('%s([11,22,33]);'% callback)
        # self.write('func([11,22,33])')
    def post(self, *args, **kwargs):
        self.write('t2.post')

settings = {
    'template_path':'views',
    'static_path':'static',
}
application = tornado.web.Application([
    (r'/index',IndexHandler),
],**settings)

if __name__ == "__main__":
    application.listen(8002)
    tornado.ioloop.IOLoop.instance().start()
app2.py
 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>Title</title>
 6 </head>
 7 <body>
 8     <input type="button" value="Ajax" onclick="DoAjax();">
 9     <input type="button" value="JsonpAjax" onclick="JsonpAjax();">
10     <script src="/statics/jquery-1.12.4.js"></script>
11     <script>
12 
13         function func(arg) {
14             console.log(arg)
15         }
16         function DoAjax() {
17             $.ajax({
18                 url: 'http://w2.com:8002/index',
19                 type: 'POST',
20                 data: {'k1': 'v1'},
21                 success:function (arg) {
22                     console.log(arg)
23                 }
24             });
25         }
26 
27         function JsonpAjax() {
28 
29 //            var tag = document.createElement("script");
30 //            tag.src = 'http://127.0.0.1:8002/index?callback=func';
31 //            document.head.appendChild(tag);
32 //            document.head.removeChild(tag);
33 
34             $.ajax({
35                 url:'http://127.0.0.1:8002/index',
36                 dataType: 'jsonp',
37                 jsonp: 'callback',
38                 jsonpCallBack: 'func'
39             })
40         }
41     </script>
42 </body>
43 </html>
index.tml
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <input type="button" value="Ajax" onclick="DoAjax();"/>
    <input type="button" value="JsonpAjaxJX" onclick="JsonpAjaxJX();"/>

    <script src="/static/jquery-1.12.4.js"></script>
    <script src="127.0.0.1:8002/index/static/jquery.cookie.js"></script>
    <script>
        function func(arg) {
            console.log(arg)
        }
        function DoAjax() {
            $.ajax({
                url:'http://127.0.0.1:8002/index',
                type:'post',
                data:{'k1':'v1'},
                success:function (arg) {
                    console.log(arg);
                }
            })
        }

    function list(dict) {
        console.log(dict)
    }
    function JsonpAjaxJX() {
        $.ajax({
            url:'http://www.jxntv.cn/data/jmd-jxtv2.html?callback=list',
            dataType:'jsonp',
            jsonpCallBack:'list'
        })
    }
    </script>

</body>
</html>
實例:江西衛視節目表

CORS(客戶端不變,服務端設置響應頭)

隨着技術的發展,如今的瀏覽器能夠支持主動設置從而容許跨域請求,即:跨域資源共享(CORS,Cross-Origin Resource Sharing),其本質是設置響應頭,使得瀏覽器容許跨域請求。

* 簡單請求 OR 非簡單請求

條件:
    一、請求方式:HEAD、GET、POST
    二、請求頭信息:
        Accept
        Accept-Language
        Content-Language
        Last-Event-ID
        Content-Type 對應的值是如下三個中的任意一個
                                application/x-www-form-urlencoded
                                multipart/form-data
                                text/plain
 
注意:同時知足以上兩個條件時,則是簡單請求,不然爲複雜請求

* 簡單請求和非簡單請求的區別?

簡單請求:一次請求
非簡單請求:兩次請求,在發送數據以前會先發一次請求用於作「預檢」,只有「預檢」經過後纔再發送一次請求用於數據傳輸。

* 關於「預檢」

- 請求方式:OPTIONS
- 「預檢」其實作檢查,檢查若是經過則容許傳輸數據,檢查不經過則再也不發送真正想要發送的消息
- 如何「預檢」
     => 若是複雜請求是PUT等請求,則服務端須要設置容許某請求,不然「預檢」不經過
        Access-Control-Request-Method
 
     => 若是複雜請求設置了請求頭,則服務端須要設置容許某請求頭,不然「預檢」不經過
        Access-Control-Request-Headers

a、支持跨域,簡單請求(在服務端加響應頭,帶相應頭就能過來)

服務器設置響應頭:Access-Control-Allow-Origin = '域名' 或 '*'      *表示全部的域名均可以訪問

<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title></title>
</head>
<body>

    <p>
        <input type="submit" onclick="XmlSendRequest();" />
    </p>

    <p>
        <input type="submit" onclick="JqSendRequest();" />
    </p>

    <script type="text/javascript" src="jquery-1.12.4.js"></script>
    <script>
        function XmlSendRequest(){
            var xhr = new XMLHttpRequest();
            xhr.onreadystatechange = function(){
                if(xhr.readyState == 4) {
                    var result = xhr.responseText;
                    console.log(result);
                }
            };
            xhr.open('GET', "http://c2.com:8000/test/", true);
            xhr.send();
        }

        function JqSendRequest(){
            $.ajax({
                url: "http://c2.com:8000/test/",
                type: 'GET',
                dataType: 'text',
                success: function(data, statusText, xmlHttpRequest){
                    console.log(data);
                }
            })
        }
    </script>
</body>
</html>
html
class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.set_header('Access-Control-Allow-Origin', "http://www.xxx.com") 
        self.write('{"status": true, "data": "seven"}')
python

b、支持跨域,複雜請求

因爲複雜請求時,首先會發送「預檢」請求,若是「預檢」成功,則發送真實數據。

  • 「預檢」請求時,容許請求方式則需服務器設置響應頭:Access-Control-Request-Method
  • 「預檢」請求時,容許請求頭則需服務器設置響應頭:Access-Control-Request-Headers
  • 「預檢」緩存時間,服務器設置響應頭:Access-Control-Max-Age
<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title></title>
</head>
<body>

    <p>
        <input type="submit" onclick="XmlSendRequest();" />
    </p>

    <p>
        <input type="submit" onclick="JqSendRequest();" />
    </p>

    <script type="text/javascript" src="jquery-1.12.4.js"></script>
    <script>
        function XmlSendRequest(){
            var xhr = new XMLHttpRequest();
            xhr.onreadystatechange = function(){
                if(xhr.readyState == 4) {
                    var result = xhr.responseText;
                    console.log(result);
                }
            };
            xhr.open('PUT', "http://c2.com:8000/test/", true);
            xhr.setRequestHeader('k1', 'v1');
            xhr.send();
        }

        function JqSendRequest(){
            $.ajax({
                url: "http://c2.com:8000/test/",
                type: 'PUT',
                dataType: 'text',
                headers: {'k1': 'v1'},
                success: function(data, statusText, xmlHttpRequest){
                    console.log(data);
                }
            })
        }
    </script>
</body>
</html>
html
class MainHandler(tornado.web.RequestHandler):
    
    def put(self):
        self.set_header('Access-Control-Allow-Origin', "http://www.xxx.com")
        self.write('{"status": true, "data": "seven"}')

    def options(self, *args, **kwargs):
        self.set_header('Access-Control-Allow-Origin', "http://www.xxx.com")
        self.set_header('Access-Control-Allow-Headers', "k1,k2")
        self.set_header('Access-Control-Allow-Methods', "PUT,DELETE")
        self.set_header('Access-Control-Max-Age', 10)
python

8、上傳文件

form表單上傳文件的時候必定要記得加上  enctype="multipart/form-data"

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

import tornado.web

IMG_LIST = []

# class IndexHandler(tornado.web.RequestHandler):
#     def get(self):
#         print('asdas')
#         self.render('index.html')
#     def post(self, *args, **kwargs):
#         self.write('{"status":1,"message":"mmm"}')

class PicturesHandler(tornado.web.RequestHandler):
    def get(self, *args, **kwargs):
        self.render('pictures.html',img_list = IMG_LIST)
        # self.render('ajaxsc.html',img_list = IMG_LIST)
        # self.render('ifname.html',img_list = IMG_LIST)
        # self.render('iframe.html',img_list = IMG_LIST)
    #
    def post(self, *args, **kwargs):
        print(self.get_argument('user'))
        print(self.get_arguments('favor'))
        file_maetas = self.request.files['hahaha']
        for meta in file_maetas:
            file_name = meta['filename']
            import os
            with open(os.path.join('static','img',file_name),'wb')as up:
                up.write(meta["body"])
            IMG_LIST.append(file_name)
        self.write('{"status":1,"message":"mmm"}')


settings = {
    'template_path':"views",
    'static_path':'static',

}
application = tornado.web.Application([
    (r'/pictures',PicturesHandler)
    # (r'/ajaxsc', PicturesHandler)
    # (r'/ajaxjq', PicturesHandler)
    # (r'/ifname',PicturesHandler)
    # (r'/iframe',PicturesHandler)
],**settings)

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

 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <ul>
        {% for item in img_list %}
        <li><img style="width: 200px;height: 200px" src="/static/img/{{item}}"></li>
        {% end %}
    </ul>
    <form action="/pictures" method="post" enctype="multipart/form-data">
        <input type="text" name = "user"/>
        <h1>性格類型</h1>
        <input type="checkbox" name="favor" value="1"/>暴虐的;
        <input type="checkbox" name="favor" value="2"/>溫和的;
        <input type="checkbox" name="favor" value="3"/>傻二的;
        <input type="file" name="hahaha"/>
        <input type="submit" value="提交"/>
    </form>
     <script src="/static/jquery-1.12.4.js"></script>
    <script>
        function  UploadFile(){
            var fileobj = document.getElementById('img').files[0];
            var form = new FormData();
            form.append('user','uuu');
            form.append('favor','1');
            form.append('hahaha','fileobj');
            var xhr = new XMLHttpRequest();
            xhr.open('post','/pictures',true);
            xhr.send(form);
        }
    </script>

</body>
</html>
pictures.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <input type="file" id="img" />
    <input type="button" onclick="UploadFile();" value="提交按鈕"/>
    <script src="/static/jquery-1.12.4.js"></script>
    <script>
        function  UploadFile(){
            var fileobj = document.getElementById('img').files[0];
            var form = new FormData();
            form.append('user','uuu');
            form.append('favor','1');
            form.append('hahaha',fileobj);
            var xhr = new XMLHttpRequest();
            xhr.open('post','/ajaxsc',true);
            xhr.send(form);
        }
    </script>

</body>
</html>
ajaxsc.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <input type="file" id="img" />
    <input type="button" onclick="UploadFile();" value="提交" />
    <script src="/static/jquery-1.12.4.js"></script>
    <script>
        function UploadFile(){
            var fileObj = $("#img")[0].files[0];
            var form = new FormData();
            form.append("user", "v1");
            form.append('favor','1');
            form.append("hahaha", fileObj);
            $.ajax({
                type:'POST',
                url: '/ajaxjq',
                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>
ajaxjq.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        .hide{
            display: none;
        }
    </style>
</head>
<body>
    <form id="my_form" name="form" action="/ifname" method="POST"  enctype="multipart/form-data" >
        <div id="main">
            <input name="user"  type="text" />
            <input name="davor"  type="text" />
            <input name="hahaha" id="my_file"  type="file" />
            <input type="button" name="action" value="提交" onclick="redirect()"/>
            <iframe id='my_iframe' name='my_iframe' src=""  class="hide"></iframe>
        </div>
    </form>
    <script src="/static/jquery-1.12.4.js"></script>
    <script>
        function redirect(){
            document.getElementById('my_iframe').onload = Testt;
            //找到id爲my_iframe 設置 onload 加載完成執行Testt函數
            document.getElementById('my_form').target = 'my_iframe';
            //將my_form 目標提交到 id爲my_iframe
            document.getElementById('my_form').submit();

        }
        function Testt(ths){
            var t = $("#my_iframe").contents().find("body").text();
            console.log(t);
        }
    </script>
</body>
</html>
iframe.html
<!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
<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上傳示例
相關文章
相關標籤/搜索