tornado框架&三層架構&MVC&MTV&模板語言&cookie&session

 
web框架的本質其實就是socket服務端再加上業務邏輯處理, 好比像是Tornado這樣的框架. 有一些框架則只包含業務邏輯處理, 例如Django, bottle, flask這些框架, 它們的使用須要依賴包含socket的第三方模塊(即 wsgiref)來運行
在python中常見的web框架構建模式有如下兩種:
MVC框架:
Models 數據相關操做
Views 模板html文件
Controllers 業務邏輯

mvc相似於抽象工廠設計模式,確認了架構之後才考慮應用設計模式, 三層架構大於MVC模式
三層架構將整個項目劃分爲:表現層(UI)、業務邏輯層(BLL)、數據訪問層(DAL)

 

 


MTV框架:
Models 數據相關操做
Templates 模板html文件
Views 業務邏輯
至關於文件夾的歸類, 只是命名不一樣, 所遵循的的思想也只是大同小異



flask, django, bottle 框架中使用模板引擎Jinja2:一個模板系統爲Flask提供模板支持,其靈活,快速和安全等優勢被普遍使用。

web框架本質是socket, 經過socket發送的就是字符串,
第一塊:協議和方式
第二塊:請求頭
第三塊:發送內容
第一塊:協議和狀態
第二塊:響應頭
第三塊:響應內容
python web 框架分類:
本身的socket ====> Tornado
第三方:wsgi +框架 ===>Django, flash, bottl都是基於wsgi

Tornado:
1.
a.導入tornado模塊
b.寫類xxxxxHandler, 必須繼承tornado的模塊
c.路由系統,就是url和類的關係
d.程序運行
e.模板路勁配置和靜態文件配置

2.
寫表單<input type="text" name="use" / >提交

後臺.self.get_argument('use)

後臺返回請求:
self.render("index.html") 找到文件, 渲染, 獲得字符串
self.write("字符串") 字符串返回給用戶
self.redirect("/manager") url跳轉

3.
模板語言:
a.{%%}if else 等代碼塊
b.{{}}
c.自定義 UIMethod, UIModule

模板語言本質:
字符串形式的函數 "def execute(): xxx" compile exec

cookie:
    Tornado:
        self.set_cookie()
        self.get_cookie()
Ajax:
  本質就是瀏覽器在用戶未感知的狀況下發請求
  XMLHttpRequest ==>發請求
  jquery內部本質仍是調用這個發送
XMLHttpRequest
 
 
 

XSS是經過網頁使用JavaScript跨站腳本的攻擊是代碼注入的一種。javascript

  • 一般是經過利用網頁開發時留下的漏洞,經過巧妙的方法注入惡意指令代碼到網頁,使用戶加載並執行攻擊者惡意製造的網頁程序。
  • 攻擊成功後,攻擊者可能獲得更高的權限(如執行一些操做)、私密網頁內容、會話和cookie等各類內容。
 
import os
import time
from jinja2 import Template
模板語言配合jinja2使用
css,   js,   靜態文件須要引用
from wsgiref.simple_server import make_server  #首次得下載wsgiref模塊,
#創建動態響應函數
def application(environ,start_response):#要實現的是作一個web應用
    # 響應首行和響應頭,響應頭能夠是空,響應首行必須有內容
    start_response('200 OK', [('Content-Type', 'text/html')])
    # environ, start_response 這兩個參數是兩個形參,因此叫什麼名字都無所謂,
    # 關鍵是實參誰來調用它
    # application的調用取決於make_server在調用serve_forever的時候要跑application
    # 至關於實參是什麼取決於模塊給他放什麼參數
    # 這個wsgiref模塊裏面有個make_server類
    # application(a,b)裏面有兩個參數,a就是打包好的數據也就是上面的environ
    # 換句話來講environ裏面放的就是全部的數據,,按着http協議解析數據:environ
    # start_response:肯定響應頭的信息,,,按者http協議組裝數據:start_response
    print("environ",environ)
    path=environ.get("PATH_INFO")# 當前的請求路徑
    if path=="/login":
        with open("template/login.html","rb")as f:
            data=f.read()
        return [data]
    elif path=="/auth":

        return [b"<h1>hello</h1>"]

#相似socket建立的socket對象,綁定IP端口,開啓監聽3步
s=make_server("127.0.0.1",8084,application)
print("servering")
s.serve_forever()#相似socket創建鏈接,等待接收用戶請求


import os
def new():
    f = open(os.path.join("文件夾","文件夾下級文件","r"))
    data = f.read()
    f.close()
    return data
wsgiref
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
     //靜態文件引入寫法
    <link rel="stylesheet" href="{{start_url('commons.css')}}" />
</head>
<body>
    <h1>提交內容:</h1>
    <form method="post" action="/index">
        <input type="text" name="xxx" />
        <input type="submit" value="提交" />
    </form>
    <h1>展現內容:</h1>
    //模板語言:1.支持普通方式 2.支持代碼塊方式,  3.支持自定義方式(uimethod,uimodule)
    <ul>
        //模板語言固定寫法(普通方式), 使用for循環展現數據
        {% for item in xxoo %}
        <li>{{item}}</li>
        {% end %}
    </ul>
//轉換後的新字符串格式
    <ul>
        <li>[11]</li>
        <li>[22]</li>
    </ul>
    //靜態文件引入寫法
    <script src='{{static_url("oldboy.js")}}'></script>
</body>
</html>
模板語言
import tornado.ioloop
import tornado.web
INPUTS_LIST = []
# 建立MainHandler類繼承tornado.web.RequestHandler
class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.write("hello world")
        #1.打開s1.html文件,讀取內容(包含特殊語法)
        #2.xxoo = [11,22] && 讀取內容(包含特殊語法),結合起來
        #3.獲得新的字符串
        #4.返回給用戶
        self.render("s1.html", xxoo = INPUTS_LIST)#默認去當前目錄下找文件,渲染成html文件
    def post(self, *args, **kwargs):
        name = self.get_argument('xxx')
        #get_argument獲取前臺提交的數據,get&post均可以獲取,get在url中能夠傳輸,post只能以提交方式傳輸,二者區別還有長度安全性
        INPUTS_LIST.append(name)
        print("post")
        # self.write("hello world")
        self.render("s1.html", xxoo = INPUTS_LIST)
settings = {
    "tempalte_path": "tpl",#模板路徑配置, 至關於配了全局的變量,之後全部使用html的頁面均可以使用tempalte找到
    "static_path": "static",#靜態文件配置
    "static_url_prefix": "/sss/",
    "ui_methods" : mt,
    "ui_modules": md,


}

# 匹配用戶URL,和類,   叫:路由映射,路由系統
application = tornado.web.Application([
    (r"/index",MainHandler)
], **settings) #配置文件settings生效


if __name__ == "__main":
    #socket運行起來
    application.listen("127.0.0.1",8888)
    tornado.ioloop.IOLoop.instance().start()
tornado

import tornado.ioloop
import tornado.web
 
 
 
class MainHandler(tornado.web.RequestHandler):
    def get(self):
        # self.write("Hello, world")
        self.render('js2.html')
 
settings = {
    'template_path':'template',#路徑配置,就是自動取到該路徑下尋找文件
    'static_path':'static',#靜態文件配置,須要特殊處理
}
 
 
 
#路由映射,根據不一樣url對應到不一樣的類裏面
application = tornado.web.Application([
    (r"/index", MainHandler),
],**settings)
#第二個接收配置文件
 
if __name__ == "__main__":
    application.listen(8888)#監聽端口
    tornado.ioloop.IOLoop.instance().start()
s1代碼

import tornado.ioloop
import tornado.web
 
input_list=[]#用來接收用戶的數據
 
class MainHandler(tornado.web.RequestHandler):
    def get(self):
        # self.write("Hello, world")
        self.render('js2.html',xxxooo = input_list)
    def post(self, *args, **kwargs):
 
 
        name = self.get_argument('jjj')#根據value提取用戶輸入的名字
        input_list.append(name)
        print(name)
        #一、打開js2.html文件,讀取內容(包含特殊語法)
        #二、xxxooo = [11,22,333,44] 讀取內容(包含特殊語法)
        #三、獲得新的字符串
        #四、將獲得新的字符串返回給用戶
        self.render('js2.html',xxxooo =  input_list)
settings = {
    'template_path':'template',#路徑配置,就是自動取到該路徑下尋找文件
    'static_path':'static',#靜態文件配置,須要特殊處理
    'static_url_prefix':'/sss/',#標記文件開始的名字
}
 
 
#路由映射,根據不一樣url對應到不一樣的類裏面
application = tornado.web.Application([
    (r"/index", MainHandler),
],**settings)
#第二個接收配置文件
 
if __name__ == "__main__":
    application.listen(8888)#監聽端口
    tornado.ioloop.IOLoop.instance().start()
利用tornado框架實現數據提交,,主要是修改上面s1的代碼
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>你好</title>
    <link rel="stylesheet" href="/sss/commons.css">
</head>
<body>
 
<P style="font-size: 50px"> js3</P>
<h1>提交內容</h1>
<form method="post" action="/index">
    <input type="text" name="jjj">
    <input type="submit" value="提交">
</form>
 
 
<h1>顯示內容</h1>
<ul>
    {% for item in xxxooo %}
    <li>{{item}}</li>
    {% end %}
</ul>
<!--<script src="sss/jayzhou.js"></script>-->
</body>
</html>
js2的html文件,寫了一個表單

對於模板語言,主要有三類css

 

模板語言分爲三類:
{{}}表達式
{% if %}{% end %}
  例如:
  <ul>
    {% for item in xxxooo %}
    <li>{{item}}</li>
    {% end %}
  </ul> #模板語言裏經過for循環展現數據
自定義:
  uimethod/uimodlehtml

 

而後在上面的s1裏面配置好設置文件前端

settings = {
    'template_path':'template',#路徑配置,就是自動取到該路徑下尋找文件
    'static_path':'static',#靜態文件配置,須要特殊處理
    'static_url_prefix':'/sss/',#標記文件開始的名字
    'ui_methods':uimethod#這個是咱們導入的文件
}

在js2.html文件裏面的模板語言java

<ul>
    {% for item in xxxooo %}
    <li>{{item}}</li>
    <h2>{{func(item)}}</h2>
    {% end %}
<h2>{{func(amp)}}</h2>

js2.html文件裏面要這樣寫python

<ul>
    {% for item in xxxooo %}
    <li>{{item}}</li>
 
    {% end %}
<h2>{{func(amp)}}</h2>
<h3>{% module custom() %}</h3>
    <!--調用自定義的custom模塊-->
 
</ul>

 

# wsgiref在py2中運行正常, 在py3中會報錯

# 當咱們將執行的index()和news()功能函數放進Controllers業務邏輯處理模塊, \
# 將返回結果ret改成文件讀寫後的內容, 並將該文件放置到Views或者Template模塊中, \
# 就造成了最基礎版本的MVC和MTV框架

from wsgiref.simple_server import make_server

def index():
    return "This is index "

def news():
    return "welcome to news "

URLS = {
    '/index': index,
    '/news': news,
}

def RunServer(rq, rp):#RunServer(rq, rp) 該方法中rq封裝了請求信息, rp封裝了響應信息
    rp('200 OK', [('Content-Type', 'text/html')])
    url = rq['PATH_INFO']#獲取請求的url鏈接地址
    if url in URLS.keys():
        ret = URLS[url]()#根據請求的url執行對應的函數
    else:
        ret = '404'
    return ret


if __name__ == '__main__':
    http = make_server('', 8000, RunServer)#這裏建立socket服務端, 並傳入業務邏輯功能函數RunServer(rq, rp)
    http.serve_forever()#啓動服務端, 阻塞進程等待客戶端訪問, 一旦有訪問則執行RunServer(rq, rp)方法
使用wsgiref再加上本身寫的業務邏輯自定義一個web框架

 

.body {
    margin: 0;
    background-color: cornflowerblue;
}
commons.css文件內容
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>S1</title>
    <link rel="stylesheet" href="../static/commons.css">
</head>
<body>
<form method="post">
    <input type="text" name="name">
    <input type="submit" value="提交">
</form>
<h1>內容展現</h1>
<ul>
    {% for item in contents %}
        <li>{{item}}</li>
    {% end %}
</ul>
</body>
</html>
index.html文件內容
import tornado.web, tornado.ioloop

# 客戶端第一次訪問調用的是`MyHandle`類中的`get(self, *args, **kwargs)`方法, 服務端向客戶端返回`index.html`文件
# - 客戶端瀏覽器接受到`index.html`文件以後, 在輸入框中輸入內容並提交以後會調用`post(self, *args, **kwargs)`, 並將輸入的內容追加到
# - `self.get_argument('name')` 獲取指定參數的內容
# `CONTENTS_LIST`中, 服務端返回`index.html`, 返回過程當中`Toranado`
# 會將`CONTENTS_LIST` 的內容渲染到`index.html`以後纔會發給客戶端瀏覽器

class MyHandle(tornado.web.RequestHandler):
    def get(self, *args, **kwargs):
        self.render("index.html", contents=CONTENTS_LIST)

    def post(self, *args, **kwargs):
        CONTENTS_LIST.append(self.get_argument('name'))
        self.render('index.html', contents=CONTENTS_LIST)


if __name__ == '__main__':
    CONTENTS_LIST = []#爲存放的是輸入框輸入的內容
    settings = {    #字典表示的是配置文件
        'template_path': 'template',#模板文件的存放位置
        'static_path': 'static', #靜態文件的存放位置, 靜態文件必須聲明, 不然瀏覽器沒法找到靜態文件
        'static_url_prefix': 'static/', #靜態文件前綴, 減小每一個文件引入都要加前綴的麻煩
    }

    application = tornado.web.Application([
        (r"/index", MyHandle)
    ], **settings)
    application.listen(800)#設置服務端的監聽端口
    tornado.ioloop.IOLoop.instance().start()#阻塞服務端進程, 等待客戶端的訪問
index.py文件內容

 

模板引擎的使用:jquery

def test_uimethod(self):
    return "uimethod"
uimethod
from tornado.web import UIModule

class MyClass(UIModule):
    def render(self, *args, **kwargs):
        return "uimodule"
uimodule.py文件以下
import tornado.web, tornado.ioloop
import uimethod as ut
import uimodule as ud


class MyHandle(tornado.web.RequestHandler):
    def get(self, *args, **kwargs):
        self.render("index.html", ag="this is ag", contents=CONTENTS_LIST)

    def post(self, *args, **kwargs):
        CONTENTS_LIST.append(self.get_argument('name'))
        self.render('index.html', contents=CONTENTS_LIST)


if __name__ == '__main__':
    CONTENTS_LIST = []
    settings = {
        'template_path': 'template',
        'static_path': 'static',
        'static_url_prefix': 'static/',
        'ui_methods': ut,
        'ui_modules': ud
    }

    application = tornado.web.Application([
        (r"/index", MyHandle)
    ], **settings)
    application.listen(80)
    tornado.ioloop.IOLoop.instance().start()
index.py文件以下
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>S1</title>
    <link rel="stylesheet" href='{{static_url("commons.css")}}'>
</head>
<body>
<h1>{{ag}}</h1>
<h1>{{test_uimethod()}}</h1>
<h1>{%module MyClass()%}</h1>
<form method="post">
    <input type="text" name="name">
    <input type="submit" value="提交">
</form>
<h1>內容展現</h1>
<ul>
    {% for item in contents %}
    <li>{{item}}</li>
    {% end %}
</ul>
<hr>
</body>
</html>
index.html文件以下

  • 模板引擎中的{{key}}表示取key對應的值, 當key爲函數時候執行該函數並取該函數結果. 例如index.html文件中的<h1>{{ag}}</h1> 實際上取得是index.pyself.render("index.html", ag="this is ag", contents=CONTENTS_LIST)中的參數ag的值
  • <h1>{{test_uimethod()}}</h1> 這裏執行的是自定義函數, 咱們將這個自定義函數寫在uimethod.py文件中, 而且在index.py文件中導入, 而後將index.py文件中的settings配置增長一行'ui_methods': ut, 該行內容表示模板引擎可執行自定義函數
  • 模板引擎中的{%%}可用於循環語句和條件語言以及自定義類的執行, {% for item in contents %}此處正是用於循環遍歷contents中的內容
  • <h1>{%module MyClass()%}</h1>此處表示模板引擎執行自定義類, 該類的文件對應的是uimodule.py文件, 咱們須要在index.pysettings中增長一行'ui_modules': ud, 改行表示模板引擎可以使用自定義類
  • 注意, 咱們將index.html文件引入css的方式改成了<link rel="stylesheet" href='{{static_url("commons.css")}}'>, static_url()是模板引擎內置的自定義函數, 用該函數引入css文件時候, 僅當css文件內容發生變化時候, 瀏覽器纔會從新緩存該css文件

 

 

 

 

 

 

 如下tornado_cookie:web

import tornado.ioloop
import tornado.web
import time
class IndexHandler(tornado.web.RequestHandler):
    def get(self, *args, **kwargs):
        self.render("index.html")

class ManagerHandler(tornado.web.RequestHandler):
    def get(self, *args, **kwargs):
        co = self.get_cookie("auth")
        if co == "1":
            self.render("manager.html")
        else:
            self.redirect("/login")


class LoginHandler(tornado.web.RequestHandler):
    def get(self, *args, **kwargs):
        self.render("login.html",status_text="")

    def post(self, *args, **kwargs):
        username = self.get_argument("username",None)
        pwd = self.get_argument("password",None)
        check = self.get_argument("auth",None)
        if username == "alex" and pwd == "sb":
            if check:
                # self.get_secure_cookie()#原生cookie
                self.set_cookie("username",username,expires_days=7)
                self.set_cookie("auth","1",expires_days=7)
            else:
                r = time.time() + 10#當前時間加10秒
                self.set_cookie("auth","1",expires=r)
                self.set_cookie("username",username,expires=r)
            self.redirect("/manager")
        else:
            self.render("login.html",status_text="登陸失敗")

class LogoutHandler(tornado.web.RequestHandler):
    def get(self, *args, **kwargs):
        self.set_cookie("auth","1",expires=time.time())
        self.redirect("/login")

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

application = tornado.web.Application([
    (r"/index", IndexHandler),
    (r"/login",LoginHandler),
    (r"/manager",ManagerHandler),
    (r"/logout",LogoutHandler),
],**settings)

if __name__ == "__main__":
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()
tornodo_cookie
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form action="/login" method="post">
        <input type="text" name="username" />
        <input type="password" name="password" />
        <input type="checkbox" name="auth" value="1">7天免登陸
        <input type="submit" value="登陸">
        <span style="color:red;">{{status_text}}</span>
    </form>
</body>
</html>
login
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <a href="/logout">退出</a>
    <h1>你的當前銀行卡餘額:-1000</h1>
</body>
</html>
manager
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>首頁</h1>
</body>
</html>
index
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

        <input type="text" name="username" />
        <input type="password" name="password" />
        <input type="checkbox" name="auth" value="1">7天免登陸
        <input type="button" value="登陸">
<script src="{{static-url('jquery-3.3.1')}}"></script>
<script>
    function SubmitForm(){
        $.post('/login',{'username':$('#user').val(), 'password':$('#pwd').val()},function(callback){
            console.log(callback);
        })
    }
</script>
jquery發送請求

 

 

 

start.py文件:

from controllers import home
import tornado.web
import tornado.ioloop
settings = {
    'template_path': 'views',#模板路勁配置
    'static_path':'statics',#靜態文件
}

application = tornado.web.Application([
    # (r'/index',home.IndexHandler),
    (r'/index/(?P<num>\d*)/(?P<nid>\d*)',home.IndexHandler),
],**settings)


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



home.html文件:

import tornado.web
class IndexHandler(tornado.web.RequestHandler):
    def get(self, nid,num):
        print(nid,num)
        self.write("ok")
正則路由匹配 

路由系統:

Tornado中支持兩種路由系統, 正則路由系統以及二級域名路由系統ajax

# 默認路由系統, 根據url的不容調用不一樣的類
application = tornado.web.Application([
    (r"/index/(?P<page>\d*)", home.IndexHandle),
], **settings)

#二級路由匹配
application.add_handlers("test.ming.com",[
    (r"/index/(?P<page>\d*)", home.IndexHandle)
])
  • (r"/index/(?P<page>\d*)", home.IndexHandle) 這裏咱們訪問時候須要以相似http://127.0.0.1/index/2的方式訪問, 在get或者post接受處理請求的函數應有page參數來接受訪問地址最後的整數(咱們稍後將根據這個作一個分頁的demo)
  • 當咱們加入二級域名時候, 默認訪問網站執行第一個默認路由系統, 僅當咱們訪問設置的二級域名纔會調用對應的處理器(當前代碼指的是test.ming.com的域名)

接下來咱們使用基於正則的路由系統來實現網頁分頁功能.redis

 

 

#利用全局變量模擬數據庫全部內容
USER_LIST= [
    {'username': 'test', 'email': 'test@163.com'}
]

#利用循環生成多條數據來模擬大量數據依次便於實現分頁效果
for i in range(300):
    tmp = {'username': "test - " + str(i), 'email': str(i) + "@vip.com"}
    USER_LIST.append(tmp)
all.py文件以下
# 單獨用於實現分頁功能的類
class Page:
    # current_page表示當前頁數, all_item表示總的數據條目
    # 初始化時候將當前頁數current_page與總頁數all_page加入到對象中
    def __init__(self, current_page, all_item):
        # all_page表示總頁數, 每頁顯示5條數據, more表示餘數, 若是大於0則表示應多加一頁才能顯示完全部的數據
        all_page, more = divmod(all_item, 5)
        if more > 0:
            all_page += 1
        self.all_page = all_page

        # 捕捉異常, 防止傳入非法字符冒充頁數, 一旦發生異常則直接將當前頁current_page設置爲1, 表示默認顯示第一頁
        try:
            current_page = int(current_page)
        except Exception as e:
            current_page = 1

        # 若是前傳入的頁數小於1, 則直接默認爲第一頁
        if current_page < 1:
            current_page = 1
        self.current_page = current_page

    # 根據當前頁在每一個頁面顯示11個頁碼, 此處是開始頁碼
    @property
    def start_page(self):
        return (self.current_page - 1) * 5

    # 結束頁碼
    @property
    def end_page(self):
        return self.current_page * 5

    # 顯示的頁碼對應的html字符串, base_url表示可定製的url跳轉路徑
    def page_str(self, base_url):
        # 定義list_page列表用來暫時存儲全部的頁碼字符串
        list_page = []

        # 若是總頁數小於11頁, 則直接顯示全部頁數
        if self.all_page < 11:
            s = 0
            e = self.all_page

        # 總頁數大於11頁時候
        else:
            # 當前頁數小於6則直接顯示1到11頁
            if self.current_page <= 6:
                s = 1
                e = 11

            # 當前頁數大於6頁時候
            else:
                # 當前頁加上5也以後就大於總頁數則直接顯示倒數11頁
                if self.current_page + 5 > self.all_page:
                    s = self.all_page - 10
                    e = self.all_page

                # 當前頁數大於6頁而且加上5頁並不超過總頁數時候, 顯示當前頁先後5頁以及當前頁
                else:
                    s = self.current_page - 5
                    e = self.current_page + 5

        # 首頁設置
        first_page = '<a href="/%s/1">首頁</a>' % (base_url)
        list_page.append(first_page)

        # 上一頁設置
        # 當前頁小於等於第一頁時候, 點擊上一頁不作任何操做(這裏理論上是不會有小於的狀況)
        if self.current_page <= 1:
            pre_page = '<a href="javascript:void(0);">上一頁</a>'

        # 當前頁大於第一頁, 點擊上一頁則直接跳轉到上一頁
        else:
            pre_page = '<a href="/%s/%s">上一頁</a>' % (base_url, self.current_page - 1)
        list_page.append(pre_page)

        # 根據上邊條件過濾後的頁碼起始位置s以及頁碼終止位置e來生成對應的11條頁碼對應的html字符串
        for p in range(s, e + 1):
            if p == self.current_page:
                tmp = '<a class="active" href="/index/%s">%s</a>' % (p, p)
            else:
                tmp = '<a href="/%s/%s">%s</a>' % (base_url, p, p)
            list_page.append(tmp)

        # 下一頁設置
        # 下一頁要大於或者等於最大頁數時候, 不作任何操做
        if self.current_page >= self.all_page:
            next_page = '<a href="javascript:void(0);">下一頁</a>'
        else:
            next_page = '<a href="/%s/%s">下一頁</a>' % (base_url, self.current_page + 1)
        list_page.append(next_page)

        # 尾頁設置
        last_page = '<a href="/%s/%s">尾頁</a>' % (base_url, self.all_page)
        list_page.append(last_page)

        # 頁面跳轉
        jump_page = """<input type="text" /><a onclick='JumpTo("%s",this)'>GO</a>""" % base_url
        # 頁面跳轉的js代碼, 本質就是location.href的使用
        jspt = """<script>
            function JumpTo(base_url,th){
                var val=th.previousElementSibling.value;
                if(val.trim().length>0){
                    location.href="/"+base_url+"/"+val
                }
            }
        </script>"""
        list_page.append(jump_page)
        list_page.append(jspt)
        return "".join(list_page)
pager.py文件以下
import tornado.web
from commons.all import USER_LIST
from commons.pager import Page

class IndexHandle(tornado.web.RequestHandler):
    def get(self, c_page):
        # c_page表示url傳遞過來的當前頁數, len(USER_LIST)表示總共有多少條數據
        pg = Page(c_page, len(USER_LIST))
        #開始頁數
        start = pg.start_page
        #尾頁
        end = pg.end_page
        #當前顯示的數據片斷
        current_list = USER_LIST[start:end]
        #傳入index並返回對應的下面分頁部分的html代碼
        str_page = pg.page_str('index')
        #將當前顯示的數據片斷, 當前頁數以及分頁的html代碼返回給瀏覽器
        self.render('index.html', list_info=current_list, current_page=pg.current_page, str_page=str_page)

    def post(self, c_page):
        #獲取傳入的username的值
        username = self.get_argument('username', None)
        #獲取傳入的email的值
        email = self.get_argument("email", None)
        #生成臨時的字典對象, 表示一個完整的數據片斷
        tmp = {'username': username, 'email': email}
        #將該數據片斷加入的全局變量USER_LIST, 這裏用全局變量模擬數據庫獲取的數據
        USER_LIST.append(tmp)
        self.redirect('/index/' + c_page)
home.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: aquamarine;
        }
        .pager a.active {
            background-color: crimson;
            color: aliceblue;
        }
    </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 tmp in list_info%}
    <tr>
        <td>{{tmp['username']}}</td>
        <td>{{tmp['email']}}</td>
    </tr>
    {%end%}
    </tbody>
</table>
<div class="pager">
    <!--加raw 以此來直接執行原始的字符串, 不作轉義-->
    {%raw str_page%}
</div>
</body>
</html>
index.html文件以下
import tornado.web,tornado.ioloop
from controllers import home

if __name__ == '__main__':
    settings = {
        # 模板路徑配置
        'template_path': 'views',
    }

    application = tornado.web.Application([
        (r"/index/(?P<c_page>\d*)", home.IndexHandle),
    ], **settings)
    application.listen(80)
    tornado.ioloop.IOLoop.instance().start()
start.py文件以下

 

 

 

 

 

 

模板引擎:

基本使用
    繼承,extends    頁面總體佈局用繼承
    導入,include    若是是小組件等重複的那麼就用導入

Tornado框架中, 模板引擎能帶給咱們不少方便, 它是便捷展示頁面的極佳方式. 在上一節中咱們介紹了模板引擎對於{{}}以及對於 {%%}的用法. 咱們簡單回顧一下:

**{{}}使用: **

  • 直接取服務端在render()函數中傳遞參數的值, 例如服務端中有self.render('index.html', contents=CONTENTS_LIST), 在html文件中有{{contents}} 則表示在html中取服務端的CONTENTS_LIST的內容
  • 咱們經過配置'ui_methods': 須要執行的自定義python模塊,以後, 咱們能夠在html文件中經過{{自定義python模塊中的函數名()}}來執行對應的函數取得該函數的返回結果以此來顯示

**{%%}的使用: **

  • {%for tmp in iterable%} 用於循環語句, 注意要加上{%end%}結束語句
  • {%if condition%} 用於條件判斷, 一樣同上須要結束語句
  • 經過配置ui_modules : 須要執行的python模塊 以後, 咱們能夠在文件中經過{%module python模塊名()%}來直接執行該模塊中對應的方法, 注意該模塊須要繼承tornado.web.UIModule

以上有不懂的請參照上一篇博客(Tornado框架01-入門總概)中的具體實例實現後再對應解釋來理解

接下來咱們老規矩, 先使用一下模板引擎的繼承以後, 再詳細介紹

 

import tornado.web


class IndexHandle(tornado.web.RequestHandler):
    def get(self, *args, **kwargs):
        self.render("extend/index.html")


class AccountHandle(tornado.web.RequestHandler):
    def get(self, *args, **kwargs):
        self.render("extend/account.html")
home
{%extends "../template/master.html"%}

<!--自定義css具體內容-->
{%block tm_css%}
    <style type="text/css">
        .page-content{
            background-color: green;
        }
    </style>
{%end%}

<!--#自定義的文本內容-->
{%block tm_content%}
    <h1>This is Account</h1>
{%end%}

<!--#自定義的js文件-->
{%block tm_js%}
    <script type="text/javascript">
        console.log('This is Account')
    </script>
{%end%}
acount.html
{%extends "../template/master.html"%}

<!--對應的自定義css的具體內容-->
{%block tm_css%}
    <style type="text/css">
        .page-content{
            background-color: yellow;
        }
    </style>
{%end%}

<!--自定義的文本內容-->
{%block tm_content%}
    <h1>This is Index</h1>
{%end%}

<!--自定義的js文件-->
{%block tm_js%}
    <script type="text/javascript">
        console.log('This is Index')
    </script>
{%end%}
index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Master</title>
    <style type="text/css">
        * {
            margin: 0px;
            padding: 0px;
        }
        .page-header{
            height: 50px;
            background-color: red;
        }
        .page-content {
            height: 200px;
            background-color: azure;
        }
        .page-footer{
            height: 50px;
            background-color: black;
        }
    </style>

    <!--爲後邊自定義的css命名並佔位置-->
    {%block tm_css%}{%end%}
</head>
<body>
    <div class="page-header"></div>
    <div class="page-content">
        <!--自定義的內容, 命名並佔位-->
        {%block tm_content%}{%end%}
    </div>
    <div class="page-footer"></div>

    <!--自定義的js文件位置-->
    {%block tm_js%}{%end%}
</body>
</html>
master.html
import tornado.web, tornado.ioloop
from controllers import home

if __name__ == '__main__':
    CONTENTS_LIST = []
    settings = {
        'template_path': 'views',

    }

    application = tornado.web.Application([
        (r"/index", home.IndexHandle),
        (r"/account", home.AccountHandle),
    ], **settings)

    application.listen(80)
    tornado.ioloop.IOLoop.instance().start()
start.py

  • 從運行結果來看, 兩個網頁的主體結構相同, 只是裏邊包含的css具體樣式, 具體內容以及js文件不一樣
  • 要繼承模板文件來使用咱們要在當前文件首行寫上{%extends "../template/master.html"%} , 這裏表示當前文件以master.html來進行渲染

{%block tm_css%}
<style type="text/css">
.page-content{
background-color: yellow;
}
</style>
{%end%}```
index.html的這部分其實就是master.htmltm_css的具體內容

  • master.html文件中{%block tm_css%}{%end%}至關與爲後面具體要寫入的內容作一個佔位符, 而且起名爲tm_css.

 使用模板的繼承能夠重複使用相同結構的模板, 能夠大大減小代碼量. 可是有時候並非全部的網頁結構都是我須要的, 咱們會想要單獨包含全部網頁都有的相同的一小部份內容. 此時就須要模板文件的包含來實現

<form>
    <input type="text">
    <input type="submit" value="提交">
</form>
{%extends "../template/master.html"%}

<!--對應的自定義css的具體內容-->
{%block tm_css%}
    <style type="text/css">
        .page-content{
            background-color: yellow;
        }
    </style>
{%end%}

<!--自定義的文本內容-->
{%block tm_content%}
    <h1>This is Index</h1>
    {%include "../include/form.html"%}
    {%include "../include/form.html"%}
    {%include "../include/form.html"%}
{%end%}

<!--自定義的js文件-->
{%block tm_js%}
    <script type="text/javascript">
        console.log('This is Index')
    </script>
{%end%}
將上面的index.html修改以下

cookie:

 cookie:在瀏覽器端保存鍵值對,       特性:每次http請求都會附加在請求中併發送給服務器端

 

import tornado.web


class IndexHandle(tornado.web.RequestHandler):
    def get(self):
        username = self.get_argument('u', None)
        if not username:
# 設置未加密的cookie, 鍵爲'name', 值爲test
            self.set_cookie('name', 'test')
 #設置加密cookie, 鍵爲'user', 值爲test.
# 設置加密cookie咱們須要在配置中添加自定義的加密串(俗稱對加密結果加鹽)"cookie_secret": 'test-secret,'
            self.set_secure_cookie('user', 'test')
            self.redirect('/admin')

    def post(self):
        pass


class AdminHandle(tornado.web.RequestHandler):
    def get(self, *args, **kwargs):
#獲取指定key未加密的cookie的值
        name = self.get_cookie('name', None)
#獲取指定key的加密後的cookie的值
        user = self.get_cookie('user', None)
        print('name: ', name, "\nuser: ", user)


# 對於set_cookie()和set_secure_cookie()都用如下常見參數
# name 表示傳入cookie的鍵
# value 表示傳入cookie的name對應的值
# domain=None 表示域名
# expires=None 設置過時時間, 這裏單位爲秒
# path="/" 表示當前的cookie在那些路徑下有效, /表示當前域名下全部的路徑均有效
# expires_days=None 設置過時時間, 單位爲天
home
import tornado.web, tornado.ioloop
from controllers import home

if __name__ == '__main__':
    settings = {
        # 模板路徑配置
        'template_path': 'views',
        "cookie_secret": 'test-secret,'
    }

    application = tornado.web.Application([
        (r"/index", home.IndexHandle),
        (r"/admin", home.AdminHandle),
    ], **settings)
    application.listen(803)
    tornado.ioloop.IOLoop.instance().start()
start

加密cookie的加密和解密原理: 

 

  • 解密時候將加密cookie中的base64(test)也就是加密後的值和時間戳再加上cookie_secret生成新的加密串和加密cookie中的加密串比較, 若相同則合法驗證經過, 而後再經過反解加密base64(test)取其原本的值

 

JavaScript操做Cookie

 

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

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
    <h1>adasd</h1>
    <script>
        /*
設置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();
}
    </script>
</body>
</html>


而後在瀏覽器中訪問
setCookie("k22=11",5)            設置cookie,超時時間爲5秒
undefined
document.cookie                查看cookie
"k1=999; k22=11= 5"
document.cookie                5秒後查看
"k1=999"





<!--toUTCString() 方法可根據世界時 (UTC) 把 Date 對象轉換爲字符串,並返回結果-->
<!--設置cookie,指定秒數過時-->
        <!--current_date.getSeconds()      獲取當前秒-->
        <!--current_date.setSeconds        設置秒-->
        <!--data.setDate(data.getDate()+7),表示獲取超過如今7天的時間-->
        <!--current_date                  當前時間+5秒-->
        <!--toUTCString()                   當前統一時間-->
HTML代碼

對於參數

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

 jquery中設置cookie

 要用須要下載  這裏

一、    導入jquery
二、    導入jQuery Cookie Plugin v1.4.1
注意點:
    若是用jquery導入的時候expires這裏若是爲數字的時候,表示天數
    若是不想用天數,那麼要用,這裏的超時時間必需要用toUTCString()統一時間

current_date.setSeconds(current_date.getSeconds() + 5); 用天數,而後用字符串拼接的方式";expires="+ current_date.toUTCString()
等來設置時間,js數組的.join方法是吧數組變成字符串
            $.cookie(「k1」,」22」,{「path」:」」,」domin」:」」,expires=1})
            上面的cookie中的數組,在內部用了join方法分割成了字符串

tornado支持兩種方式

  • 1、簡單的方式
  • 2、簽名的方式
首先服務端讓瀏覽器端生成cookie的時候會通過base64加密,首先生成加密串,
注意這裏的當前時間是
>>> import time
>>> time.time()
1491471613.5271676     --->生成的這個值就是當前時間
>>>
加密串 =v1(value)+當前時間+內部自定義字符串
以後生成的這個cookie就是k1(key)=v1|加密串|當前時間

如何驗證這個cookie有沒有被篡改:
客戶端向瀏覽器端發送請求:會把v1和加密串和當前時間發送給瀏覽器,瀏覽器內部會通過md5生成一個新的加密串  
自定義字符串+發送過來的時間+v1等於新的加密串,而後加密串進行對比,若是一致就能經過
import tornado.ioloop
import tornado.web

class IndexHandler(tornado.web.RequestHandler):
    #這裏判斷判斷用戶登陸
    def get(self):
        if self.get_argument("u",None) in ["aa","eric"]:
            self.set_cookie("name",self.get_argument("u"))
            # self.set_secure_cookie("name",self.get_argument("u"))
        else:
            self.write("請登陸")

class ManagerHandler(tornado.web.RequestHandler):
    #若是有cookie的時候就登陸
    def get(self):
        if self.get_cookie("name",None) in ["aa","eric"]:
            self.write("歡迎登陸:"+self.get_cookie("name"))
        else:
            self.redirect("/index")

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

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

# 上面就是用一種簡單的模式登陸,登陸的時候
# 在瀏覽器中輸入
# http://127.0.0.1:8000/index?u=aa
# 以後就會執行IndexHandler方法中的get方法首先存入用戶輸入的cookie,對比後臺,而後訪問manager網站的時候,判斷,若是有對應的cookie那麼就會出現歡迎登陸
基於cookie實現用戶驗證
import tornado.ioloop
import tornado.web

class IndexHandler(tornado.web.RequestHandler):
    #這裏判斷判斷用戶登陸
    def get(self):
        if self.get_argument("u",None) in ["alex","eric"]:
# 這裏設置加密的cookie
            self.set_secure_cookie("user",self.get_argument("u"))
        else:
            self.write("請登陸")


class ManagerHandler(tornado.web.RequestHandler):
    #若是有cookie的時候就登陸
        def get(self):
# 獲取加密的cookie
             if str(self.get_secure_cookie("user",None),encoding="utf-8") in ["alex","eric"]:
                self.write("歡迎登陸:"+str(self.get_secure_cookie("user")))
             else:
                self.redirect("/index")

settings={
    "template_path":"views",
    "static_path":"statics",
# 這必須設置配置
    "cookie_secret":"hello",
}
application=tornado.web.Application([
    (r"/index",IndexHandler),
    (r"/manager",ManagerHandler)
],**settings)

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

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

簽名Cookie的本質是:

寫cookie過程:

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

讀取 簽名 + 加密值
對簽名進行驗證
base64解密,獲取值內容
注:許多API驗證機制和安全cookie的實現機制相同

 

 

session:

咱們將許多信息放在cookie中勢必會形成瀏覽器端的臃腫, 此時便須要在服務端保存本來在瀏覽器端的那些鍵值對. 在瀏覽器端只需存儲一個表示身份的隨機加密字符串, 當瀏覽器端訪問服務端時候攜帶該字符串, 通過比較, 驗證合法以後即可以取該用戶在服務端存儲的相應信息. 可是在Tornado中並無session的模塊, 咱們須要自定義來實現.
session其實就是定義在服務器端用於保存用戶回話的容器,其必須依賴cookie才能實現。
全部的web框架都是session[key]=value的方法實現的
 

優化後代碼:

 

在Tornado框架中,默認執行Handler的get/post等方法以前默認會執行 initialize方法,因此能夠經過自定義的方式使得全部請求在處理前執行操做

這裏的initialize就是鉤子函數

#定義tornado中的鉤子函數和反射函數來優化下面的類
class BaseHandler(tornado.web.RequestHandler):
    def initialize(self):
        self.session=Session(self)

class IndexHandler(BaseHandler):
    #這裏判斷判斷用戶登陸,get方法是被反射調用的getattr
    def get(self):
        if self.get_argument("u",None) in ["aa","eric"]:
            self.session.set_value("is_login",True)
            self.session.set_value("name",self.get_argument("u",None))
        else:
            self.write("請登陸")

class ManagerHandler(BaseHandler):
    def get(self):
        val=self.session.get_value("is_login")
        if val:
            self.write(self.session.get_value("name"))
        else:
            self.write("請從新登陸")
兩個類繼承一個共同的父類,利用tornado內置的鉤子函數來優化代碼
 
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import tornado.ioloop
import tornado.web

#這個字典必須定製成爲全局變量用來保存用戶的信息,若是是局部變量,
# 那麼http請求斷開下次用戶登陸這個用戶信息就會消失
container={}

#把Sesson封裝起來
class Session:
    def __init__(self,handler):
        self.handler=handler
        self.random_str=None  #用戶鏈接初始化隨機數
    def __genarate_random_str(self):   #建立隨機字符串
        import hashlib
        import time
        #首先經過md5生成隨機數據,電腦中的數據都是16進制保存的
        obj=hashlib.md5()
        ## 加入自定義參數來更新md5對象
        obj.update(bytes(str(time.time()),encoding="utf-8"))
        # 獲得加嚴後的十六進制隨機字符串來做爲用戶的索引
        random_str=obj.hexdigest()
        return random_str

    def __setitem__(self,key,value):
        #這裏判斷若是服務端沒有隨機數
        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   #最後把上面判斷出來的隨機數傳遞給類
        container[self.random_str][key]=value
        #這裏是爲寫超時時間作準備
        self.handler.set_cookie("__kakaka__",self.random_str)  #設置cookie給瀏覽器,這裏能夠設置超時時間


    def __getitem__(self,key):  #獲取值, 注意加密方式返回的cookie的值是bytes類型的
        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)   #前面若是都知足,有值就拿值,沒有就爲None
        return value

#定義tornado中的鉤子函數和反射函數來優化下面的類
class BaseHandler(tornado.web.RequestHandler):
    def initialize(self):
        self.session=Session(self)

class IndexHandler(BaseHandler):
    #這裏判斷判斷用戶登陸,get方法是被反射調用的getattr
    def get(self):
        if self.get_argument("u",None) in ["aa","eric"]:
            self.session["is_login"]=True
            self.session["name"]=self.get_argument("u",None)
            # self.session.set_value("is_login",True)
            # self.session.set_value("name",self.get_argument("u",None))
        else:
            self.write("請登陸")

class ManagerHandler(BaseHandler):
    def get(self):
        val=self.session["is_login"]
        if val:
            self.write(self.session["name"])
        else:
            self.write("請從新登陸")


settings={
    "template_path":"views",
    "static_path":"statics",
    "cookie_secret":"hello",
}
application=tornado.web.Application([
    (r"/index",IndexHandler),
    (r"/manager",ManagerHandler)
],**settings)

if __name__=="__main__":
    application.listen(8003)
    tornado.ioloop.IOLoop.instance().start()
__getitem__/__setitem__優化後
用戶若是直接鏈接manager會提示必須登陸,主要緣由是瀏覽器cookie中沒有登陸信息
1、    用戶訪問index網頁的時候就是訪問IndexHandler這個類,用戶鏈接,服務器內部就會初始化隨機數
二、    服務器就會執行set_value方法,而且傳入參數is_login參數,首先爲了判斷用戶是否第一次登錄,因此用if not self.random_str,沒有就用get_cookie()方法去客戶端中拿隨機數,這裏須要判斷,若是客戶端也沒有隨機數,那麼服務端就要本身建立隨機數,而且把這個隨機數傳遞給服務器這個類;若是客戶端有隨機數,要判斷這個隨機數是不是僞造的,若是是僞造的,服務器須要本身建立隨機數,而且把這個隨機數傳遞給服務器這個類;以後把is_login參數替代key傳遞給session這個字典求出來value這個值,而且設置一下這個cookie傳遞給瀏覽器;而後設置key爲name的cookie
三、    用戶訪問manager這個網站,會執行get方法,而且獲取瀏覽器隨機數,若是瀏覽器中沒有隨機數或者瀏覽器的隨機數是僞造的,那麼就會退出,若是通過了2這個步驟,那麼就能登陸成功而且獲得設置cookie中key爲name的值
流程詳解

 

驗證碼:

驗證碼原理在於後臺自動建立一張帶有隨機內容的圖片,而後將內容經過img標籤輸出到頁面。
這個驗證碼是放在tornado的session裏面的
驗證碼機制:服務器首先建立驗證碼,而且把驗證碼放入到隨機數這個字典裏面,用戶經過get方法接收到驗證碼,而後用戶輸入驗證碼和帳戶信息發送給服務器,服務器經過對比用戶發來的驗證碼和本身產生的驗證碼,(這裏要建立不分辨大小寫,可讓用戶輸入的和本身產生的轉成所有大寫或者所有小寫)對比,若是同樣那麼就顯示登陸成功,若是沒有同樣,那麼就顯示輸入的驗證碼錯誤。而且在前端添加一個點擊事件,只要用戶一點擊那麼驗證碼就會刷新

pip3 install pillow 安裝圖像處理模塊
<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title></title>
    <style>
        .aa{
            cursor: pointer;
        }
    </style>
</head>
<body>
    <h1>請輸入登陸信息</h1>
    <form action="/login" method="post">
        <p><input name="user" type="text" placeholder="用戶名" /></p>
        <p><input name="pwd" type="password" placeholder="密碼" /></p>
        <p>
            <input name='code' type="text" placeholder="驗證碼" />
            <img class="aa" src="/check_code" onclick='ChangeCode();' id='imgCode'>
        </p>
        <input type="submit" value="提交"/><span style="color: red">{{status}}</span>
    </form>
   <script type="text/javascript">

       function ChangeCode() {
            var code = document.getElementById('imgCode');
            //url後面只能添加問號,添加問號就是改變地址
            code.src += '?';
        }
    </script>
</body>
</html>
login.html
#/usr/bin/env python
#-*- coding:utf-8-*-
import tornado.ioloop
import tornado.web
import tornado.httpserver
import tornado.ioloop
import tornado.process
import tornado.web
#


#這個字典必須定製成爲全局變量用來保存用戶的信息,若是是局部變量,
# 那麼http請求斷開下次用戶登陸這個用戶信息就會消失
container={}

        #把Sesson封裝起來
class Session:
    def __init__(self,handler):
        self.handler=handler
        self.random_str=None  #用戶鏈接初始化隨機數
    def __genarate_random_str(self):   #建立隨機字符串
        import hashlib
        import time
        #首先經過md5生成隨機數據,電腦中的數據都是16進制保存的
        obj=hashlib.md5()
        obj.update(bytes(str(time.time()),encoding="utf-8"))
        random_str=obj.hexdigest()
        return random_str
    def __setitem__(self,key,value):
        #這裏判斷若是服務端沒有隨機數
        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   #最後把上面判斷出來的隨機數傳遞給類
        container[self.random_str][key]=value
        #這裏是爲寫超時時間作準備
        self.handler.set_cookie("__kakaka__",self.random_str)  #設置cookie給瀏覽器,這裏能夠設置超時時間


    def __getitem__(self,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)   #前面若是都知足,有值就拿值,沒有就爲None
        return value

#定義tornado中的鉤子函數和反射函數來優化下面的類
class BaseHandler(tornado.web.RequestHandler):
    def initialize(self):
        self.session=Session(self)

class IndexHandler(BaseHandler):
    #這裏判斷判斷用戶登陸,get方法是被反射調用的getattr
    def get(self):
        if self.get_argument("u",None) in ["aa","eric"]:
            self.session["is_login"]=True
            self.session["name"]=self.get_argument("u",None)
            # self.session.set_value("is_login",True)
            # self.session.set_value("name",self.get_argument("u",None))
        else:
            self.write("請登陸")

class ManagerHandler(BaseHandler):
    def get(self):
        val=self.session["is_login"]
        if val:
            self.write(self.session["name"])
        else:
            self.write("請從新登陸")
# class CheckCodeHandler(BaseHandler):
#     def get(self):
#         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())
class MainHandler(BaseHandler):
    def get(self):
        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.redirect("/login")
            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()
        #將圖片內容寫入到IO中mstream
        img.save(mstream, "GIF")
        #爲每一個用戶保存其對應的驗證碼
        self.session["CheckCode"] = code
        self.write(mstream.getvalue())

settings={
    'template_path': 'views',
    'static_path': 'static',
    "static_url_prefix":"/statics/",
    "cookie_secret":"hello",
    # "xsrf_cookies":True,
}

application=tornado.web.Application([
    (r"/index",IndexHandler),
    (r"/manager",ManagerHandler),
    # (r"/login",LoginHandler),
    (r"/login",MainHandler),
    (r"/check_code",CheckCodeHandler),


],**settings)

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

 下載下面源碼以後,須要把check_code.py和Monaco.ttf導入到這個代碼目錄中(僅僅限制與python3.5)

驗證碼Demo源碼下載:猛擊這裏
 

CSRF:

會集羣要會:分佈式哈希haxi redis
CSRF限制post請求的
用戶訪問是先請求服務器調用get請求,而後發送post請求,以後服務器會給用戶一個隨機字符串,當用戶離開後,下次再訪問會帶着這個隨機字符串訪問服務器,若是用戶沒有這個隨機字符串,那麼CSRF會阻止這個用戶請求,這樣可使服務器免遭受惡意攻擊形成服務器宕機

要加上CSRF:

一、在配置文件中加上配置文件」xsrf_cookies」:True

二、在前臺代碼中加上{% raw xsrf__form_html %}

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',
    'static_path': 'static',
    "static_url_prefix":"/statics/",
    "cookie_secret":"hello",
    "xsrf_cookies":True,   這裏加上配置文件
}

application=tornado.web.Application([
    (r"/index",IndexHandler),
    (r"/manager",ManagerHandler),
    # (r"/login",LoginHandler),
    (r"/login",MainHandler),
    (r"/check_code",CheckCodeHandler),
    (r"/csrf",CsrfHandler)


],**settings)
View Code
<form action="/csrf" method="post">
    {% raw xsrf_form_html() %}
    <p><input type="text" placeholder="用戶"/></p>
    <p><input type="text" placeholder="密碼"/></p>
    <p>
        <input name="code" type="text" placeholder="驗證碼"/>
        <!--<img src="/check_code">-->
    </p>
    <input type="submit" value="Submit"/>
html

提交的是AJAX的post請求

若是你提交的是 AJAX 的 POST 請求,你仍是須要在每個請求中經過腳本添加上 _xsrf 這個值。下面是在 FriendFeed 中的 AJAX 的 POST 請求,使用了 jQuery 函數來爲全部請求組添加 _xsrf 值:

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 + ")"));
    }});
};

對於 PUT 和 DELETE 請求(以及不使用將 form 內容做爲參數的 POST 請求) 來講,你也能夠在 HTTP 頭中以 X-XSRFToken這個參數傳遞 XSRF token。

若是你須要針對每個請求處理器定製 XSRF 行爲,你能夠重寫 RequestHandler.check_xsrf_cookie()。例如你須要使用一個不支持 cookie 的 API, 你能夠經過將 check_xsrf_cookie() 函數設空來禁用 XSRF 保護機制。然而若是 你須要同時支持 cookie 和非 cookie 認證方式,那麼只要當前請求是經過 cookie 進行認證的,你就應該對其使用 XSRF 保護機制,這一點相當重要。

 

一、cookie和session的區別
1)cookie是保存在客戶端的,session是保存在服務端的,由於服務器端,表示可能在內存中,可能在數據庫端,可能在緩存中統稱爲服務器端
二、session和cookie有什麼聯繫?:
答:有。session是經過cookie人爲構建起來的,在web開發裏面自己沒有session這個東西的。在服務器端能夠高層一個數據庫,能夠在內存中搞成一個字典,每一次用戶來訪問的時候,給用戶發一對token,下一次,用戶訪問再帶着這一對token來,服務器端就知道你是否是上一次的你。若是再問就來畫一張圖

三、分頁 XSS  跨站腳本攻擊
四、CSRF/別名XSRF工做方式:
答:跨站請求僞造,  理解爲:攻擊者盜用了你的身份,以你的名義發送惡意請求
驗證:第一次請求的時候是get方式請求,防止沒有通過驗證就來post請求,形成大併發機器宕機
五、    Ajax
爲何要有Ajax
答:防止頁面批量刷新
    利用:
        iframe   忽略
        XMLHttpRequest
            本身寫
            xhr
            xhr.open()
            xhr.onreadystatechange
            xhr.send()
        jQuery
        會用下面的就會jquery,ajax
            $.ajax({
            url:
            type
            data
            dataType
            success
            error
})
六、    驗證碼、
七、    上傳文件
form標籤
        form標籤 enctype=「「form標籤裏面必需要有這個才能進行上傳文件
經過Ajax上傳文件
        利用formDate()
            XMLHttpRequest
            jQuery
        iframe+form標籤爲了兼容性設計,ifram就至關於設置一個通道,form把數據提交到這個通道,而後不刷頁面上傳文件

 

tornado 結合前端進行文件上傳:

在表單中咱們獲取用戶提交的數據,使用的是get_argument,複選框使用的是get_arguments,可是文件的不同,文件的使用request.files。

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
注意:

form文件上傳,必定要在form表單上設置enctype的參數。enctype="multipart/form-data"。否則上傳沒法成功。
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(8003)
    tornado.ioloop.IOLoop.instance().start()
PYthon
說明:

1、代碼中self.request封裝了全部發送過來請求的內容。

2、self.request.files:能夠獲取上傳文件的全部信息。此方法獲取的是一個生成器,內部是由yield實現的,所以咱們在利用此方法返回的對象的時候,不能經過下標獲取裏面的對象,只能經過迭代的方法。

3、迭代出來的對象的filename:就表示上傳文件的文件名。

四、迭代出來的對象的body:表示上傳文件的內容。獲取的文件內容是字節形式的。

ajax上傳文件

  • 原生ajax
  • jquery

原生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
說明:

代碼中利用原生的ajax進行文件上傳。

關鍵點:

一、獲取文件對象,經過files[0],獲取當前上傳的文件對象。

二、經過FormData(),實例化一個對象form對象。

三、而後將要傳遞的參數,文件以鍵和值以逗號分隔的形式append到form對象中去。

四、而後將整個form對象發送到服務端。

注意:

後臺代碼和上面的代碼同樣,不變。注意接收的文件名要同步。

jquery文件上傳:

<!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
說明:

一、和原生的同樣,都是顯得獲取當前上傳文件的對象。files[0];而後實例化form對象,將要傳遞的內容append到實例化的對象form中。

二、後臺代碼同前,注意字段名對應。

關鍵點:

processData:false和contentType:false。這2個是關鍵。

默認的jquery會將咱們上傳的數據作部分處理。上面兩段代碼,就是告訴jquery不要處理咱們的文件,否則會將咱們的文件處理得不完整。

 

 

iframe文件上傳

 

原生的ajaxjquery上傳的時候,咱們都是經過實例化一個form對象來進行文件的上傳。可是實例化這個form的對象並非全部的瀏覽器都存在,好比低版本的IE就可能沒有合格FormData對象,那上面的方法就存在兼容性,沒有form對象就不能發送。所以的使用一個兼容性更好的來進行操做,iframe

<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
    <form id="my_form" name="form" action="/index" method="POST"  enctype="multipart/form-data" >
        <div id="main">
            <input name="fff" id="my_file"  type="file" />
            <input type="button" name="action" value="Upload" onclick="redirect()"/>
            <iframe id='my_iframe' name='my_iframe' src=""  class="hide"></iframe>
        </div>
    </form>
 
    <script>
        function redirect(){
            document.getElementById('my_iframe').onload = Testt;
            document.getElementById('my_form').target = 'my_iframe';
            document.getElementById('my_form').submit();
 
        }
         
        function Testt(ths){
            var t = $("#my_iframe").contents().find("body").text();
            console.log(t);
        }
    </script>
</body>
</html>
html
關鍵點:

一、document.getElementById('my_form').target = 'my_iframe':這段代碼就是獲取iframe標籤。

     target就是目標,只要給form設置了target的話,form提交的時候,就會提交到這個target指定的目標上。因此上面的代碼表示只要form提交,就會提交到iframe上去。

二、當iframe操做完後會執行Testt方法,Testt方法就是獲取後臺返回的信息,並打印。

 Jsonp實現ajax跨域請求

同源策略

瀏覽器有一個很重要的概念——同源策略(Same-Origin Policy)。所謂同源是指,域名,協議,端口相同。不一樣源的客戶端腳本(javascript、ActionScript)在沒明確受權的狀況下,不能讀寫對方的資源。比較特別的是:因爲同源策略是瀏覽器的限制,因此請求的響應和發送是能夠進行的,只不過瀏覽器不支持罷了.

  • 限制:XmlHttpRequest,  AJax
  • 不限制:img iframe script塊等等具備src屬性的標籤

 JSONP(JSON with Padding)

是JSON的一種」使用模式」,可用於解決主流瀏覽器的跨域數據訪問的問題。因爲同源策略,通常來講位於 server1.example.com 的網頁沒法與不是 server1.example.com的服務器溝通,而 HTML 的script 元素是一個例外。利用 <script> 元素的這個開放策略,網頁能夠獲得從其餘來源動態產生的 JSON 資料,而這種使用模式就是所謂的 JSONP。用 JSONP 抓到的資料並非 JSON,而是任意的JavaScript,用 JavaScript 直譯器執行而不是用 JSON 解析器解析

 

基本思路

 

利用script標籤,src導入目標域名的接口,在文檔數的head標籤中添加一行script標籤,獲得內容後將scprit標籤刪除,返回的解析後的參數即爲獲得的數據.

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

    <input type="button" onclick="Ajax();" value="普通AJax"/>
    <input type="button" onclick="Ajax2();" value="跨域普通AJax"/>
    <input type="button" onclick="Ajax3();" value="跨域牛逼AJax"/>
    <input type="button" onclick="Ajax4();" value="江西TV"/>

    <script src="/static/jquery-2.1.4.min.js"></script>
    <script>
            // 原生ajax,測試無效
        function Ajax(){
            $.ajax({
                url: '/get_data/',
                type: 'POST',
                data: {'k1': 'v1'},
                success: function (arg) {
                    alert(arg);
                }
            })
        }
            // 使用ajax跨域請求,測試無效
        function Ajax2(){
            $.ajax({
                url: 'http://wupeiqi.com:8001/api/',//需修改
                type: 'GET',
                data: {'k1': 'v1'},
                success: function (arg) {
                    alert(arg);
                }
            })
        }
        
        // 利用script標籤,獲得數據
        function Ajax3(){
            // script
            // alert(api)
            var tag = document.createElement('script');
            tag.src = 'http://wupeiqi.com:8001/api/';//需修改
            document.head.appendChild(tag);
            document.head.removeChild(tag);
        }
        function fafafa(arg){
            console.log(arg);
        }
        
        // 例子,獲取江西衛視的節目單
        function Ajax4(){
            // script
            // alert(api)
            var tag = document.createElement('script');
            tag.src = 'http://www.jxntv.cn/data/jmd-jxtv2.html?callback=list&_=1454376870403';
            document.head.appendChild(tag);
            document.head.removeChild(tag);
        }
        function list(arg){
            console.log(arg);
        }
    </script>
</body>
</html>
利用script標籤實現跨域代碼

JSONP實現ajax跨域

以上的代碼其實也是jsonp的基本思路

$.ajax({
    url:..
    type: 'GET',
    dataType: 'jsonp',
    //傳遞給請求處理程序或頁面的,用以得到jsonp回調函數名的參數名(通常默認爲:callback)
    jsonp: 'callback',
    //自定義的jsonp回調函數名稱,默認爲jQuery自動生成的隨機函數名,也能夠寫"?",jQuery會自動爲你處理數據
    jsonpCallback: 'list'
})
    
function list(arg){
    
}
基本的jsonp寫法
jsonp: callback      #發送給請求處理程序的,被請求端經過request.GET.get("callback"),得到jsonp回調函數的參數

jsonpCallback: 'list' #定義回調函數的名稱,而後後面經過list(...)來處理獲取數據
<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title></title>
</head>
<body>

    <p>
        <input type="button" onclick="Jsonp1();"  value='提交'/>
    </p>

    <p>
        <input type="button" onclick="Jsonp2();" value='提交'/>
    </p>

    <script type="text/javascript" src="jquery-1.12.4.js"></script>
    <script>
        function Jsonp1(){
            var tag = document.createElement('script');
            tag.src = "http://c2.com:8000/test/";
            document.head.appendChild(tag);
            document.head.removeChild(tag);

        }

        function Jsonp2(){
            $.ajax({
                url: "http://c2.com:8000/test/",
                type: 'GET',
                dataType: 'JSONP',
                success: function(data, statusText, xmlHttpRequest){
                    console.log(data);
                }
            })
        }


    </script>
</body>
</html>
生產示例-###基於JSONP實現跨域Ajax - Demo

JSONP不能發送POST請求

究其根源,經過script標籤的src屬性進行跨域請求,<script src='http://www.jxntv.cn/data/jmd-jxtv2.html?callback=qwerqweqwe&_=1454376870403'>最後所有都會轉換成GET請求,哪怕是你把type改成POST.

其它例子參考

其餘ajax跨站請求方式

須要順帶提一句的是,跨站請求還有另外一種方式:cors,跨站資源共享,但此中方式對瀏覽器版本有要求,IE8如下的均不支持.

CORS與JSONP相比,無疑更爲先進、方便和可靠。

 一、 JSONP只能實現GET請求,而CORS支持全部類型的HTTP請求。

 二、 使用CORS,開發者可使用普通的XMLHttpRequest發起請求和得到數據,比起JSONP有更好的錯誤處理。

 三、 JSONP主要被老的瀏覽器支持,它們每每不支持CORS,而絕大多數現代瀏覽器都已經支持了CORS(這部分會在後文瀏覽器支持部分介紹)。

CORS(跨域資源共享)

背後的原理是:使用自定的HTTP頭部與服務器進行溝通,從而由服務器決定響應是否成功

它容許瀏覽器向跨源(協議 + 域名 + 端口)服務器,發出XMLHttpRequest請求,從而克服了AJAX只能同源使用的限制。

瀏覽器將CORS請求分紅兩類:簡單請求(simple request)和非簡單請求(not-so-simple request)。

只要同時知足如下兩大條件,就屬於簡單請求。
1) 請求方法是如下三種方法之一:
HEAD
GET
POST
2)HTTP的頭信息不超出如下幾種字段:
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

基於cors實現AJAX請求:

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
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"}')
tornado

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

c 、跨域獲取響應頭

默認獲取到的全部響應頭只有基本信息,若是想要獲取自定義的響應頭,則須要再服務器端設置Access-Control-Expose-Headers。

<!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);
                    // 獲取響應頭
                    console.log(xhr.getAllResponseHeaders());
                }
            };
            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);
                    // 獲取響應頭
                    console.log(xmlHttpRequest.getAllResponseHeaders());
                }
            })
        }


    </script>
</body>
</html>
HTML
class MainHandler(tornado.web.RequestHandler):
    
    def put(self):
        self.set_header('Access-Control-Allow-Origin', "http://www.xxx.com")

        self.set_header('xxoo', "seven")
        self.set_header('bili', "daobidao")

        self.set_header('Access-Control-Expose-Headers', "xxoo,bili")


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

d、跨域傳輸cookie

在跨域請求中,默認狀況下,HTTP Authentication信息,Cookie頭以及用戶的SSL證書不管在預檢請求中或是在實際請求都是不會被髮送。

若是想要發送:

  • 瀏覽器端:XMLHttpRequest的withCredentials爲true
  • 服務器端:Access-Control-Allow-Credentials爲true
  • 注意:服務器端響應的 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.withCredentials = true;
    
                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'},
                    xhrFields:{withCredentials: true},
                    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.set_header('Access-Control-Allow-Credentials', "true")
            
            self.set_header('xxoo', "seven")
            self.set_header('bili', "daobidao")
            self.set_header('Access-Control-Expose-Headers', "xxoo,bili")
    
            self.set_cookie('kkkkk', 'vvvvv');
    
            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)
    Tornado
相關文章
相關標籤/搜索