Python全棧開發之1八、cookies、session和ajax等相關知識

1、cookies

  本質爲在瀏覽器端保存的鍵值對,由服務端寫在瀏覽器端,之後每次請求的時候,瀏覽器都攜帶着cookie來訪問,cookies的使用之處很是多,好比用戶驗證,登錄界面,右側菜單隱藏,控制頁面列表顯示條數等,已經後面的session都是基於cookie的。cookie從設置方面來講能夠由tronado和前端js設置javascript

tornado設置(普通字符串,tronado作了分割等處理)html

  self.cookies
  self.get_cookie('k1')
  self.set_cookie('k2', 'v2')
瀏覽器js設置
  document.cookie
  document.cookie.split(";") 獲取全部的cookie列表
  document.cookie = "k3=66" 設置
  document.cookie = "k3=66;path='/"前端

下面來看一下用tornado設置的代碼 java

#!/usr/bin/env python
# coding=utf-8

import tornado.web
import tornado.ioloop


class IndexHandler(tornado.web.RequestHandler):
    def get(self, *args, **kwargs):
        print(self.cookies)
        print(self.get_cookie('k1'))
        self.set_cookie('k2', '999')
        self.render('index.html')


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

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

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

下面看一下html代碼裏面是怎麼經過js來設置的python

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

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

     function setCookieByDays(name, value, expires) {
        var current_date = new Date();
        current_date.setDate(current_date.getDate()+expires);
        document.cookie = name + '= ' + value +';expires=' + current_date.toUTCString();
    }

    //此外還能夠導入jquery.cookie.js後經過
    // $.cookie('k1','v1',{expires:7});設置過時時間爲7天
</script>
</body>
</html>

上面說的是最基本的cookie,採用的是鍵值對,稍微高級一點作法對value進行加密,下若是進行加密的話,先來看一下加密流程 jquery

加密cookie流程web

  服務端 
    v1=base64加密(v1) 
    v1| v1+時間戳+自定義密鑰 以後在通過相似md5加密變成下面的
    v1| 加密字符串 | 時間戳 保存到客戶端ajax

  客戶端來請求,服務端先base64解密拿到 v1而後根據客戶端發來的時間戳+v1+自定義密鑰再次生成加密字符串和客戶端發來的加密字符串進行對比,看是否一致。數據庫

這裏看一下tornado設置和獲取加密的方法,set_secure_cookieget_secure_cookie,此外記得在settings裏面配置cookie_secret密鑰就行,具體演示和上面的相似,就不具體的演示了。json

2、session  

  cookie保存單一鍵值對,若是須要保存其餘內容,則須要寫多個cookie,而每次請求的話都會發送全部的cookie這樣的話,會形成網絡擁堵,此外,cookies是保存在瀏覽器端,若是保存用戶名密碼的cookie也放在瀏覽器端的話,也不夠安全,這樣就引出了session,session是人爲生成的。session也是基於cookie來作的,可是隻在瀏覽器端生成一個cookie(隨機字符串,sessionId,token),而在服務端也保存着這段cookie,此外服務端還根據這段cookie能夠生成一個字典,字典裏面就能夠放置用戶的其餘信息,服務端將這段cookie(sessionId)寫到瀏覽器端,之後瀏覽器端來訪問的時候,服務端根據sessionId從其相應的字典裏面獲取相應的信息來作相應用戶認證機制。session能夠保存在全局變量裏,放在數據庫,文件裏面,memcached radis可是不能放在局部變量裏。下面看下自定義的session的代碼實現

import tornado.web
import tornado.ioloop
import hashlib
import time
CONTAINER = {

}

class Session:
    def __init__(self, handler):
        self.handler = handler
        self.random_str = ''

    @staticmethod
    def __generate_str():
        obj = hashlib.md5()
        obj.update(bytes(str(time.time()), encoding='utf-8'))
        return obj.hexdigest()

    def __getitem__(self, item):
        random_str = self.handler.get_cookie('__session__')
        if not random_str:
            return None
        if random_str not in CONTAINER.keys():
            return None
        return CONTAINER[random_str].get(item, None)

    def __setitem__(self, key, value):
        random_str = self.handler.get_cookie('__session__')
        if not random_str:
            random_str = Session.__generate_str()
            # self.handler.set_cookie('__session__', random_str)
            CONTAINER[random_str] = {}
        else:
            if random_str not in CONTAINER.keys():
                random_str = Session.__generate_str()
                CONTAINER[random_str] = {}
        self.random_str = random_str
        CONTAINER[self.random_str][key] = value
        self.handler.set_cookie('__session__', self.random_str)  # 爲何放在這裏,cookie可能失效?放在上面失效也同樣

    def __delitem__(self, key):
        random_str = self.handler.get_cookie('__session__')
        if not random_str:
            return None
        if random_str not in CONTAINER.keys():
            return None
        del CONTAINER[random_str][key]

Tornado框架中,默認執行Handler的get/post等方法以前默認會執行 initialize方法,因此能夠經過自定義一個basehandler類實現initialize的方法,使得全部在繼承basehandler的類中,能夠自行的完成一個初始化的工做。

3、Xss和csrf  

Xss跨站腳本攻擊

  惡意攻擊者往Web頁面裏插入惡意Script代碼,當用戶瀏覽該頁之時,嵌入其中Web裏面的Script代碼會被執行,從而達到惡意攻擊用戶的特殊目的。

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

  這裏主要看下html裏面的代碼,用js獲取_xsrf對應的session,而後用jquery的ajax發送post請求,而且將_xsrf的session也發送過去。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="/sss/jquery-1.12.4.js"></script>
    <!--<script src="{{ static_url('jquery-1.12.4.js') }}" ></script>-->
</head>
<body>
    <!--{{ xsrf_form_html() }}-->
    {% raw xsrf_form_html() %}

    <input type="button" value="ajax_csrf" onclick="SubmitCsrf();">

    <script>
     
        function getCookie(name) {
            var r = document.cookie.match("\\b" + name + "=([^;]*)\\b");
            return r ? r[1] : undefined;
        }

        function SubmitCsrf() {
            var nid = getCookie('_xsrf');
            console.log(nid);
            $.post({
                url: '/csrf',
                data:{'k1':'v1', "_xsrf":nid},
                success:function (callback) {
                    console.log(callback);
                }
            });
        }
    </script>
</body>
</html> 

這樣通過上面的處理以後,服務端就只須要寫簡單的handler類就能夠了,這裏就再也不多敘述了。 

4、隨機驗證碼圖片  

登錄註冊的時候,須要驗證碼的功能,原理爲在後臺自動建立一張隨機圖片,而後經過img標籤輸出到前端。這裏咱們須要安裝一個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

隨機生成驗證碼圖片模塊

下面首先來看一下html文件,

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="statics/jquery-1.12.4.js"></script>
</head>
<body>
    <form action="login" method="post">
        <input type="text", name="code">
        <img src="/check_code" onclick="ChangeCode();" id="imgcode">
        <input type="submit" value="submit">
        <span>{{status}}</span>
    </form>
    <script>
        function ChangeCode() {
            var code = document.getElementById('imgcode');
            code.src += '?'
        }
    </script>
</body>
</html>

再來看一下,服務端是怎麼寫的

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

這裏須要注意一點,要記得將驗證碼寫到session中經行保存,這樣後面能夠進行驗證。這樣一個簡單的隨機碼驗證圖片就完成了。

5、自定義分頁類  

 分頁的功能很常見,下面將其整理爲一個類,相似session類同樣,之後使用只需稍做修改在頁面嵌入爲原生字符串便可。

class Pagenation:
    def __init__(self, current_page, all_item, each_item):

        all_pager, c = divmod(all_item, each_item)
        if c > 0:
            all_pager += 1
        if current_page == '':
            current_page = 1
        self.current_page = int(current_page)  # 當前頁
        self.all_pages = all_pager  # 總的頁面數
        self.each_item = each_item  # 每頁顯示的item數

    @property
    def start_item(self):  # 當前頁的起始item位置
        return (self.current_page - 1) * self.each_item

    @property
    def end_item(self):  # 當前頁結束item位置
        return self.current_page * self.each_item

    @property
    def start_end_span(self):  # 獲取開始和結束頁的具體數字
        if self.all_pages < 10:
            start_page = 1  # 起始頁
            end_page = self.all_pages + 1  # 結束頁
        else:  # 總頁數大於10
            if self.current_page < 5:
                start_page = 1
                end_page = 11
            else:
                if (self.current_page + 5) < self.all_pages:
                    start_page = self.current_page - 4
                    end_page = self.current_page + 5 + 1
                else:
                    start_page = self.all_pages - 10
                    end_page = self.all_pages + 1
        return start_page, end_page

    def generate_str_page(self):
        list_page = []
        start_page, end_page = self.start_end_span

        if self.current_page == 1:  # 上一頁
            prev = '<li><a class="pre-page" href="javascript:void(0);">上一頁</a></li>'
        else:
            prev = '<li><a class="pre-page" href="/index/%s">上一頁</a></li>' % (self.current_page - 1,)
        list_page.append(prev)

        for p in range(start_page, end_page):  # 1-10
            if p == self.current_page:
                temp = '<li><a class="li-page" href="/index/%s">%s</a></li>' % (p, p)
            else:
                temp = '<li><a href="/index/%s">%s</a></li>' % (p, p)
            list_page.append(temp)

        if self.current_page == self.all_pages:  # 下一頁
            nex = '<li><a class="next-page" href="javascript:void(0);">下一頁</a></li>'
        else:
            nex = '<li><a class="next-page" href="/index/%s">下一頁</a></li>' % (self.current_page + 1,)
        list_page.append(nex)

        # 跳轉
        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

6、AJAX 

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

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

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

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

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

<!DOCTYPE html>
<html>

    <head lang="en">
        <meta charset="UTF-8">
        <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模擬ajax

現在使用ajax其實就是使用瀏覽器(除了老舊的IE6,IE5)的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對象的主要屬性:
<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title></title>
</head>
<body>

    <h1>XMLHttpRequest - Ajax請求</h1>
    <input type="button" onclick="XhrGetRequest();" value="Get發送請求" />
    <input type="button" onclick="XhrPostRequest();" 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>

XmlHttpRequest發送ajax請求
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 方法列表

Jquery之ajax方法
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

    <h1>Jquery - Ajax請求</h1>
    <input type="button" onclick="XhrGetRequest();" value="Get發送請求" />
    <input type="button" onclick="XhrPostRequest();" value="Post發送請求" />

    <script src="/statics/jquery-1.12.4.js"></script>
    <script >
        function XhrGetRequest() {
           $.get({
               url: "/test/",
               data: {'k1': 'v1'},
               success:function (callback) {
                   console.log(callback)
               }
           })
        }
        function XhrPostRequest() {
            $.post({
                url: "/test/",
                data: {'k1':'v1'},
                success:function (callback) {
                    console.log(callback)
                }
            })
        }
    </script>

</body>
</html>

jquery發送ajax請求

7、跨域ajax  

因爲瀏覽器存在同源策略機制,本域腳本只能讀寫本域內的資源,而沒法訪問其它域的資源,請求的發送和響應是能夠進行的,可是瀏覽器不接受而已,可是瀏覽器同源策略並非對全部的請求均制約:

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

若是要實現跨域請求,有兩種方法,jsonp和cors(跨域資源共享)下面來看一下怎麼用這兩種方法來實現跨域請求

1.JSONP實現跨越請求

jsonp的原理是利用script標籤的src實現跨域。這種方法很是巧妙,可是有一個問題那就是隻能發get請求

下面來看一下文件這裏本地用了兩個域w1,和w2,下面來看一下具體的文件

<!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="JsonpAjax" onclick="JsonpAjax();">
    <script src="/statics/jquery-1.12.4.js"></script>
    <script>

        function func(arg) {
            console.log(arg)
        }
        function DoAjax() {
            $.ajax({
                url: 'http://w2.com:8002/index',
                type: 'POST',
                data: {'k1': 'v1'},
                success:function (arg) {
                    console.log(arg)
                }
            });
        }

        function JsonpAjax() {

//            var tag = document.createElement("script");
//            tag.src = 'http://w2.com:8002/index?callback=func';
//            document.head.appendChild(tag);
//            document.head.removeChild(tag);

            $.ajax({
                url:'http://w2.com:8002/index',
                dataType: 'jsonp',
                jsonp: 'callback',
                jsonpCallBack: 'func'
            })
        }
    </script>
</body>
</html>

w1下面的index用來請求w2
#!/usr/bin/env python
# coding=utf-8

import tornado.web
import tornado.ioloop


class IndexHandler(tornado.web.RequestHandler):
    def get(self):
        func = self.get_argument('callback')
        self.write('%s([11,22])' % func)

    def post(self, *args, **kwargs):
        self.write('w2.post')


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

application = tornado.web.Application([
    (r'/index', IndexHandler),

], **settings)

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

w2下面的start文件
#!/usr/bin/env python
# coding=utf-8

import tornado.web
import tornado.ioloop


class IndexHandler(tornado.web.RequestHandler):
    def get(self):
        self.render('index.html')

    def post(self, *args, **kwargs):
        self.write('w1.post')


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

application = tornado.web.Application([
    (r'/index', IndexHandler),

], **settings)

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

w1下的start文件

二、CORS

隨着技術的發展,如今的瀏覽器能夠支持主動設置從而容許跨域請求,即:跨域資源共享(CORS,Cross-Origin Resource Sharing),其本質是設置響應頭,使得瀏覽器容許跨域請求。這種作法是去請求的域不用作修改,只需被請求的域須要修改。

說cors以前,先要分清楚什麼是簡單請求和複雜請求。

條件:

     1 、請求方式:HEAD、GET、POST
     2 、請求頭信息:
         Accept
         Accept - Language
         Content - Language
         Last - Event - ID
         Content - Type  對應的值是如下三個中的任意一個
                                 application / x - www - form - urlencoded
                                 multipart / form - data
                                 text / plain
 
注意:同時知足以上兩個條件時,則是簡單請求,不然爲複雜請求
簡單請求: 一次請求
非簡單請求:兩次請求,在發送數據以前會先發一次請求用於作「預檢」,
      只有「預檢」經過後纔再發送一次請求用於數據傳輸。

一、簡單請求

簡單請求因爲沒有預檢,因此比較簡單,只須要在被請求的一方設置響應頭 Access-Control-Allow-Origin,value爲請求響應的域名,若是寫上一個*的話,表明全部的域均可以來這裏請求。

二、非簡單請求

  • 「預檢」請求時,容許請求方式則需服務器設置響應頭:Access-Control-Request-Method
  • 「預檢」請求時,容許請求頭則需服務器設置響應頭:Access-Control-Request-Headers
  • 「預檢」緩存時間,服務器設置響應頭:Access-Control-Max-Age 設置完成後再該時間裏只需經過一次預檢
  • 默認獲取到的全部響應頭只有基本信息,若是想要獲取自定義的響應頭,則須要再服務器端設置Access-Control-Expose-Headers。

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

若是想要發送:

  • 瀏覽器端:XMLHttpRequest的withCredentials爲true
  • 服務器端:Access-Control-Allow-Credentials爲true
  • 注意:此時服務器端響應的 Access-Control-Allow-Origin 不能是通配符 *

下面來看一下代碼

<!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="DoAjaxComplex" onclick="DoAjaxComplex();"/>

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

        function DoAjax() {
            $.ajax({
                url: 'http://w2.com:8002/cors',
                type: 'POST',
                data: {'k1': 'v1'},
                success: function (arg) {
                    console.log(arg);
                }
                
            });
        }
        function DoAjaxComplex() {
            $.ajax({
                url: 'http://w2.com:8002/cors',
                type: 'PUT',
                headers: {'h1': 'xxoo'},
                data: {'k1': 'v1'},
                xhrFields:{withCredentials: true},
                success: function (arg) {
                    console.log(arg);
                }

            });
        }

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

w1下的html文件
#!/usr/bin/env python
# coding=utf-8

import tornado.web
import tornado.ioloop


class IndexHandler(tornado.web.RequestHandler):
    def get(self):
        func = self.get_argument('callback')
        self.write('%s([11,22])' % func)

    def post(self, *args, **kwargs):
        self.write('w2.post')

class CorsHandler(tornado.web.RequestHandler):
    def get(self):
        self.write('{"status": 1, "message": "get"}')

    def post(self, *args, **kwargs):
        self.set_header('Access-Control-Allow-Origin', "*")
        self.write('{"status": 1, "message": "post"}')

    def options(self, *args, **kwargs):
        # 容許put方法來
        # self.set_header('Access-Control-Allow-Origin', "*")
        self.set_header('Access-Control-Allow-Origin', "http://w1.com:8001")
        self.set_header('Access-Control-Allow-Methods', "PUT,DELETE")
        self.set_header('Access-Control-Allow-Headers', "h1,h2")
        self.set_header('Access-Control-Allow-Credentials', "true")
        self.set_header('Access-Control-Max-Age', 10)

    def put(self, *args, **kwargs):
        print(self.cookies)
        self.set_cookie('k1', 'kkk')
        self.set_header('Access-Control-Allow-Origin', "http://w1.com:8001")
        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.write('ok')

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

application = tornado.web.Application([
    (r'/index', IndexHandler),
    (r'/cors', CorsHandler),

], **settings)

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

w2的start文件 

8、上傳文件

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

<div class="publish">
    <form action="publish" method="post" enctype="multipart/form-data">
        <div class="title">
            <label class="title-label">標題:</label>
            <input type="text" name="title" >
        </div>
        <div class="content1">
            <label class="content-label">內容:</label>
            <textarea  name="content"></textarea>
        </div>
        <input type="file" name='up_picture'>
        <input class="submit" type="submit" value="提交">
    </form>
</div>



class PubHandler(account.BaseHanlder):
    def get(self, *args, **kwargs):
        USER_INFO['is_login'] = self.session.get_value("is_login")
        USER_INFO['username'] = self.session.get_value("username")
        if USER_INFO['is_login']:
            self.render('publish/publish.html', user_info=USER_INFO)
        else:
            self.write('請先登陸')

    def post(self):
        title = self.get_argument('title', None)
        content = self.get_argument('content', None)

        if self.request.files:
            file_pic = self.request.files['up_picture'][0]
            # print(file_pic, file_pic['filename'], file_pic['body'])
            file_path = os.path.join('static', 'pic', 'upload', file_pic['filename'])  # 上傳的圖片保存
            print(file_path)
            with open(file_path, 'wb') as f:
                f.write(file_pic['body'])

form表單上傳文件

下面來看一下幾種ajax上傳

<!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>
        function Uploadfile() {
            var fileobj = document.getElementById('img').files[0];
            var form = new FormData();
            form.append('file_img',fileobj);

            var xhr = new XMLHttpRequest();
            xhr.open("post", '/file', true);
            xhr.send(form);
        }
    </script>
</body>
</html>

XmlHttpReq上傳文件
<!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="/statics/jquery-1.12.4.js"></script>
    <script>
        function Uploadfile() {
            var fileobj = $('#img')[0].files[0];  //
            var form = new FormData();
            form.append('file_img',fileobj);

            $.ajax({
                type: 'POST',
                url: '/file',
                data: form,
                processData: false,     // 這兩行須要加上,否則jquery會自動對傳輸的數據進行轉換
                contentType:false,
                success:function (callback) {
                    console.log(callback)
                }
            })
        }
    </script>
</body>
</html>

jquery上傳文件
<!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="/file" method="POST" enctype="multipart/form-data">
        <input type="file" id="img" name="file_img">
        <input type="button" onclick="redirect();" value="提交">
        <iframe id="my_iframe" name="my_iframe" src="" class="hide"></iframe>
    </form>


    <script src="/statics/jquery-1.12.4.js"></script>
    <script>
        function redirect() {
            document.getElementById('my_iframe').onload = callBk;
            document.getElementById('my_form').target = 'my_iframe';
            document.getElementById('my_form').submit();
        }
        
        function callBk() {
            var t = $("#my_iframe").contents().find('body').text();
            console.log(t)
        }
    </script>
</body>
</html>

利用iframe上傳文件
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="/statics/jquery-1.12.4.js"></script>
</head>
<body>
    <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", "/file");
            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="file_img" size="50" type="file" />
    <input id="formsubmit" type="submit" value="Send File" />
</form>
<div id="textarea">
</div>
</body>
</html>

基於iframe實現ajax上傳
class UploadHandeler(BaseHandler):
    def get(self, *args, **kwargs):
        # self.render("Xhr_img.html")
        # self.render("jquery_img.html")
        # self.render("iframe_img.html")
        self.render("iframe_ajax_img.html")

    def post(self, *args, **kwargs):
        if self.request.files:
            file_img = self.request.files['file_img'][0]
            with open(file_img['filename'], 'wb') as f:
                f.write(file_img['body'])
        self.write('ok')

服務端處理類
相關文章
相關標籤/搜索