環境: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>

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")')
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"
基於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
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