Django - 解決跨域的三種方式


環境:python3.6.2+Django1.11.5+pycharm2017.2.3


首先了解什么是瀏覽器同源策略?

1995年,同源政策由 Netscape 公司引入瀏覽器。目前,所有瀏覽器都實行這個政策。

最初,它的含義是指,A網頁設置的 Cookie,B網頁不能打開,除非這兩個網頁"同源"。所謂"同源"指的是"三個相同":

  • 協議相同
  • 域名相同
  • 端口相同

不受瀏覽器同源策略影響的有,img,script,iframe,它們通過其src屬性去請求數據,繞過瀏覽器同源策略,

所以,ajax也受限,但有些時候,我們不得不做一些手腳來達到ajax跨域請求操作,那么,就用到jsonp和cors了。

注意:cors和jsonp依賴於jquery,使用之前需要導入jquery。

jsonp方式:

我們在client端向server端發送一個普通的ajax請求,結果,會報一個:Access-Control-Allow-Origin的錯誤,但我們查看server端,是已經反回了,只是數據被瀏覽器攔截了,那么我們利用script的src的屬性進行請求,

我們手動創建一個script標簽, 傳遞URL,定義發送函數和接收變量的函數,但僅僅是這樣的話,不做其他操作的話,會報錯:數據未定義,因為,通過script請求過來的數據,會按照script的規則當做變量進行定義,所以,數據被script當做變量,導致報錯

那么,知道如何報錯,我們就手動給變量定義,callback=func,但是,這要server端配合對數據進行配合,不夠靈活,因為兩邊的callback都要一致,client端變一下callback,還要告訴server端同步更改,為了解決這個,我們對server端更改代碼,如示例所示,獲取到client端的callback,再進行字符串格式化,這樣,我們做好如此更改,不管client傳什么,我們就可以一勞永逸了

這樣,我們手動的實現了jsonp的原理

For exampl:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="x-ua-compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Title</title>
</head>
<body>

<h1>ajax跨域之jsonp</h1>

<input type="button" id="b1" value="向自己家發送請求">
<input type="button" id="b2" value="向別的網址發送請求">
<input type="button" id="b3" value="手動實現jsonp本質">
<input type="button" id="b4" value="手動實現jsonp本質callback">
<input type="button" id="b5" value="ajax的jsonp,自己寫同名回調函數">
<input type="button" id="b6" value="ajax的jsonp,一切從簡">

<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script>
<script>
    // 向自己家發送請求沒問題,
    $("#b1").click(function () {
        $.ajax({
            url: 'http://127.0.0.1:8844/test1/',
            type: 'GET',
            success: function (arg) {
                alert(arg)
            }
        })
    });

    // 咱們向別的網址發送請求就會遇到問題,瀏覽器同源策略影響
    $("#b2").click(function () {
        $.ajax({
            url: 'http://127.0.0.1:8033/data/',
            type: 'GET',
            success: function (arg) {
                alert(arg)
            }
        })
    });

    /*
    jsonp的的本質,利用script標簽的src屬性發送請求
    script的src屬性把返回的數據當成一個未定義的變量,所以,我們還要對這個變量做處理
    */
    $("#b3").click(function () {
        var tag = document.createElement('script');
        tag.src = 'http://127.0.0.1:8033/data2/';
        document.head.appendChild(tag)
    });

    // 利用自定義函數來獲取返回的數據

    function list(arg) {
        alert(arg);
        document.head.removeChild(tag)
    }

    $("#b4").click(function () {
        tag = document.createElement('script');
        tag.src = 'http://127.0.0.1:8033/data3/?callback=list';
        document.head.appendChild(tag)
    });


    // ajax的jsonp,這兩種方式,差不多,請求的網站只需動態加上回調函數
    //方式一,在自定義的同名回調函數里處理數據,雖然success里也可以處理

    function dict(arg) {
        alert(arg);
    }

    $("#b5").click(function () {
        $.ajax({
            url: 'http://127.0.0.1:8033/data5/',
            type: 'GET',
            dataType:'JSONP', // 傳輸方式是jsonp
            jsonp:'callback', // 等於在地址欄后面加上:?callback
            jsonpCallback:'dict', // 等於http://127.0.0.1:8033/data4/?callback=dict
        })
    });

    // 方式二,一切從簡,直接在ajax的success里處理數據
    // 要是單獨只說明datatype,則ajax會自己定義callback,但是所請求的網站包含數據的函數名,不能寫死
    // 需寫動態的,因為ajax的callback是動態的,

    $("#b6").click(function () {
        $.ajax({
            url: 'http://127.0.0.1:8033/data5/',
            type: 'GET',
            dataType:'JSONP',
            success: function (arg) {
                alert(arg)
            }
        })
    });


</script>

</body>
</html>
jsonp原理示例
from django.shortcuts import render,HttpResponse

# Create your views here.


def data(request):
    """
    正常返回,不要對數據做處理
    """
    return HttpResponse('數據66666')


def data2(request):


    # 直接返回沒有問題,但對方把數據當成未定義的變量
    # return HttpResponse('數據66666')
    # 所以,我們要配合,比如下面這樣, 但這樣太復雜了
    return HttpResponse('alert(111111)')

def data3(request):
    import time
    time.sleep(2)
    data_obj = 'qwertytffasdf'
    response = request.GET.get('callback')
    print(response)
    return HttpResponse('%s("%s")'%(response,data_obj))

def data5(request):

    data_obj = 'aaaaaaaaaaaaaaaa'
    response = request.GET.get('callback')
    print(response)
    return HttpResponse('%s("%s")' % (response, data_obj))
    # return HttpResponse('func("66666666")')
server端手動配合

jsonp本質:

   雖然依然通過ajax發送請求,但jsonp卻偷偷地通過創建一個script標簽進行數據的獲取

   過程是:在head頭內建立script標簽,獲取到數據之后,立即把script標簽刪掉

注意:

  如果在發送ajax時,只指定dataType:‘JSONP’,那么, jquery會自動的添加callback,如上面jsonp原理示例里面的方式二所示,我們無需再次定義一個函數去處理數據,直接在success里處理,如果按照方式一,可以不用寫success這個回調函數,但是,還是推薦方式二,代碼簡潔;

問題:jsonp是否支持post?

  不支持

jsonp只能發get請求

CORS(Cross-Origin Resource Sharing):跨站資源共享

cors實現思想:

  CORS背后的基本思想是使用自定義的HTTP頭部允許瀏覽器和服務器相互了解對方,從而決定請求或響應成功與否

簡單請求&&復雜請求

簡單請求 OR 非簡單請求 
條件: 
    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 
注意:同時滿足以上兩個條件時,則是簡單請求,否則為復雜請求 
簡單請求和非簡單請求的區別? 
簡單請求:
    一次請求 
非簡單請求:
    兩次請求,在發送數據之前會先發一次請求用於做“預檢”,
    只有“預檢”通過后才再發送一次請求用於數據傳輸。 
關於“預檢” 
    - 請求方式:OPTIONS 
    - “預檢”其實做檢查,檢查如果通過則允許傳輸數據,檢查不通過則不再發送真正想要發送的消息 
    - 如何“預檢” 
        如果復雜請求是PUT等請求,則服務端需要設置允許某請求,否則“預檢”不通過 
        Access-Control-Request-Method="PUT" 
        如果復雜請求設置了請求頭,則服務端需要設置允許某請求頭,否則“預檢”不通過 
        Access-Control-Request-Headers="k1"
簡單請求 OR 非簡單請求

基於cors實現的ajax請求:

1、支持跨域的簡單請求

  服務器設置響應頭:Access-Control-Allow-Origin = '指定域名' 

  服務器設置響應頭:Access-Control-Allow-Origin = '*'    # *:表示允許所有的請求

2、復雜請求的請求頭:

  由於復雜請求時,首先會發送“預檢”請求,如果“預檢”成功,則發送真實數據。   # 預檢時可以發送空數據

  “預檢”請求時,允許請求方式則需服務器設置響應頭:Access-Control-Request-Method="PUT"    # 或其他復雜請求如DELETE

  “預檢”請求時,允許請求頭則需服務器設置響應頭:Access-Control-Request-Headers="k1"     # ajax的header:{"k1":"v1"}

  “預檢”緩存時間,服務器設置響應頭:Access-Control-Max-Age=10   # 十秒內此請求再有請求過來時無需再發送options預檢,直接發送復雜請求,減輕服務器壓力

3、跨域獲取響應頭

  默認獲取到的所有響應頭只有基本信息,如果想要獲取自定義的響應頭,則需要再服務器端設置Access-Control-Expose-Headers。

4、跨域傳輸cookie

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

  如果想要發送:

    瀏覽器端:XMLHttpRequest的withCredentials為true

    服務器端:Access-Control-Allow-Credentials為true

注意:服務器端響應的 Access-Control-Allow-Origin 不能是通配符

def cors(request):
    if request.method == 'OPTIONS':
        response = HttpResponse()
        # 設置緩存時間,在請求的十秒內,無需發送options請求預檢驗證,
        response['Access-Control-Max-Age'] = 10
        # 只允許這個請求訪問
        # response['Access-Control-Allow-Origin'] = "http://127.0.0.1:8844"
        # 允許所有請求來訪問
        response['Access-Control-Allow-Origin'] = "*"

        # 允許那些復雜的請求來訪問
        response['Access-Control-Allow-Methods'] = "PUT,DELETE"
        return response
    else:
        response = HttpResponse('CORS數據')
        response['Access-Control-Allow-Origin'] = "http://127.0.0.1:8844"
        return response
響應頭

 

如果要處理多個請求的跨域,可以在中間件內處理

from django.utils.deprecation import MiddlewareMixin


class CorsMiddleware(MiddlewareMixin):
    """
    為解決跨域問題,所建立的中間件
    """

    def process_response(self, request, response):
        """
        :param response: server端向client端返回的json數據
        :return: 添加響應頭
        """
        response["Access-Control-Allow-Methods"] = "POST,GET,OPTIONS"
        response['Access-Control-Allow-Origin'] = '*'
        response["Access-Control-Max-Age"] = '100000'
        response['Access-Control-Allow-Headers'] = "content-type"
        return response
corsmiddleware

 

requests:

另外的:requests模式:

導入Python的requests模塊

requests.get("URL")

requests模式不受任意影響就能發送獲取請求

總結:

三者的優缺點:

  • cors相比jsonp支持更多的請求方式,
  • jsonp需要client和server端的相互配合,
  • jsonp比cors方式兼容性更好
  • cors在client端無需設置,server端需要針對不同的請求,來做head頭的處理
  • jsonp和cors都是在本地執行請求數據,因為打開網頁的時候,已經將js代碼下到本地瀏覽器了,然后瀏覽器負責接下來的操作
  • 而requests模式,則是瀏覽器向服務器發送請求,服務器根據請求,去拿數據,然后再返回給瀏覽器,增加服務器壓力

 

 

 

測試代碼:https://github.com/TestXX11/jsonp-cors

參考博客:http://www.cnblogs.com/wupeiqi/articles/5703697.html

參考博客:http://www.ruanyifeng.com/blog/2016/04/same-origin-policy.html


免責聲明!

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



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