跨域AJAX
本篇主要討論JSONP和CORS這兩種技術,使用它們的原因是為了完成對資源的跨域訪問,也就是如何繞過瀏覽器的同源策略Same-origin Policy。
那么什么是Same-origin Policy呢?簡單地說,在一個瀏覽器中訪問的網站不能訪問另一個網站中的數據,除非這兩個網站具有相同的Origin,也即是擁有相同的協議、主機地址以及端口。一旦這三項數據中有一項不同,那么該資源就將被認為是從不同的Origin得來的,進而不被允許訪問。
特別的:由于同源策略是瀏覽器的限制,所以請求的發送和響應是可以進行,只不過瀏覽器不接受罷了。
瀏覽器同源策略并不是對所有的請求均制約:
- 制約:?XmlHttpRequest
- 不叼:?img、iframe、script等具有src屬性的標簽
解決方案:
1、-requests發請求時,跨域無限制
2、- ajax發請求時,瀏覽器限制【是否可以繞過限制?】
-JSONP
-CORS
?
requests模塊當然可以通過跨域:
1 #####服務端(urls.py)###### 2 3 urlpatterns = [ 4 url(r'^admin/', admin.site.urls), 5 url(r'^get_data.html/$', views.get_data), 6 ]?
1 ######服務端(views.py)###### 2 from django.shortcuts import render,HttpResponse 3 4 def get_data(request): 5 return HttpResponse(‘機密文件’)?
1 #####客戶端(views.py)###### 2 3 def index(request): 4 # 方式一:requests模塊 5 import requests 6 response=requests.get("http://127.0.0.1:8000/get_data.html/") 7 return render(request,"index.html",{"response":response}) 1 #####客戶端(index.html)####### 2 3 <!DOCTYPE html> 4 <html lang="en"> 5 <head> 6 <meta charset="UTF-8"> 7 <meta http-equiv="x-ua-compatible" content="IE=edge"> 8 <meta name="viewport" content="width=device-width, initial-scale=1"> 9 <title>Title</title> 10 <script src="/static/jquery-3.2.1.js"></script> 11 </head> 12 <body> 13 <h1>JIAのHOMESITE</h1> 14{{ response.text }} 15 </body>16 </html>
當然本篇主要討論JSONP和CORS這兩種跨域技術。
一、JSONP
依據:帶有 src 屬性的標簽不受同源策略的影響(img, script, iframe等)。
我們可以通過使用 script 標簽來獲取內容,但 script 中 src 獲取內容后,獲得到的字符串(類似于調用python中的 eval 或 exec)會被 JS 執行,所以我們可以定義一個函數,然后讓服務端返回的內容外面包裹一層這個函數名,前端在訪問的時候把這個函數名發送過去,并提前定義好該函數。
當然,這種方法擁有一個顯著的缺點,那就是只支持GET操作。
?
手動實現:
服務端返回的數據
1 from django.shortcuts import render,HttpResponse 2 3 4 def get_data(request): 5 return HttpResponse('func("機密文件")')?
客戶端定義及獲取數據
1 urlpatterns = [ 2 url(r'^admin/', admin.site.urls), 3 url(r'^index.html/$', views.index), 4 url(r'^cors.html/$', views.cors), 5 ]?
1 from django.shortcuts import render 2 3 4 def index(request): 5 return render(request,"index.html")?
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta http-equiv="x-ua-compatible" content="IE=edge"> 6 <meta name="viewport" content="width=device-width, initial-scale=1"> 7 <title>Title</title> 8 <script src="/static/jquery-3.2.1.js"></script> 9 </head> 10 <body> 11 <h1>JIAのHOMESITE</h1> 12 <input type="button" value="獲取數據" οnclick="getInfo()" /> 13 14 <script> 15 var url = 'http://127.0.0.1:8000/get_data.html/'; // 服務端路徑 16 var $script; // 模擬使用創建的標簽名 17 18 function getInfo() { 19 $script = document.createElement('script'); // 創建一個 script 標簽 20 $script.setAttribute('src',url); // 將需要請求數據的地址放入 script 的 src 中 21 document.head.appendChild($script); // 將標簽放入到 head 中 22 } 23 24 function func(data) { // 數據返回后用來接收的函數 25 console.log(data); 26 document.head.removeChild($script); // 接收完數據后,從頁面刪除剛才使用的標簽 27 } 28 </script> 29 </body> 30 </html>?
通過JSONP自動完成 - 上面是它的原理
?
服務端返回的數據
1 from django.shortcuts import render,HttpResponse 2 3 4 def get_data(request): 5 # 根據后端發送過來的名字來決定返回時,數據外面套的內容 6 funcName = request.GET.get('callback') 7 return HttpResponse('%s("機密文件")'%funcName)?
客戶端定義及獲取數據
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta http-equiv="x-ua-compatible" content="IE=edge"> 6 <meta name="viewport" content="width=device-width, initial-scale=1"> 7 <title>Title</title> 8 <script src="/static/jquery-3.2.1.js"></script> 9 </head> 10 <body> 11 <h1>JIAのHOMESITE</h1> 12 13 <script> 14 function func(data) { 15 console.log(data); 16 } 17 18 $(function () { 19 var url = 'http://127.0.0.1:8000/get_data.html/'; 20 $.ajax({ 21 url:url, 22 type:'GET', 23 dataType:'JSONP', 24 jsonp:'callback', 25 jsonpCallback:'func' 26 }); 27 }) 28 </script> 29 </body> 30 </html>?
二、CORS
隨著技術的發展,現在的瀏覽器可以支持主動設置從而允許跨域請求,即:跨域資源共享(CORS,Cross-Origin Resource Sharing),其本質是設置響應頭,使得瀏覽器允許跨域請求。
CORS將導致跨域訪問的請求分為三種:Simple Request,Preflighted Request以及Requests with Credential。
簡單請求 和 非簡單請求的區別
簡單請求只發送一次,直接發送數據;
非簡單請求則會發送兩次數據,第一次發送 opption 請求(預檢),為的是查看真正的數據是否可以被接收(數據頭和請求方式是否符合要求), 如果符合要求,服務端可以把內容加入到頭文件中來告訴瀏覽器;
簡單請求 和 非簡單請求的判斷依據
條件:1、請求方式:HEAD、GET、POST2、請求頭信息:AcceptAccept-LanguageContent-LanguageLast-Event-IDContent-Type 對應的值是以下三個中的任意一個application/x-www-form-urlencodedmultipart/form-datatext/plain注意:同時滿足以上兩個條件時,則是簡單請求,否則為非簡單請求?
?關于預檢
- 請求方式:OPTIONS - “預檢”其實做檢查,檢查如果通過則允許傳輸數據,檢查不通過則不再發送真正想要發送的消息 - 如何“預檢”=> 如果復雜請求是PUT等請求,則服務端需要設置允許某請求方式,否則“預檢”不通過Access-Control-Request-Method=> 如果復雜請求設置了請求頭,則服務端需要設置允許某請求頭,否則“預檢”不通過Access-Control-Request-Headers?
?基于cors實現AJAX請求:
a、支持跨域,簡單請求
在使用cors的時候,客戶端幾乎不用修改,只需要按照普通的ajax的請求方式發送;
在服務端返回數據的時候,只要在返回的時候,加上一個響應頭就可以解決這個問題了
服務器設置響應頭:Access-Control-Allow-Origin = '域名' 或 '*'
*的話,代表允許所有的請求
?
?服務端返回的數據
1 from django.shortcuts import render,HttpResponse 2 3 4 def get_data(request): 5 if request.method == 'GET': 6 response=HttpResponse('機密文件') 7 response['Access-Control-Allow-Origin'] = '*' 8 return response?
?客戶端定義及獲取數據
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta http-equiv="x-ua-compatible" content="IE=edge"> 6 <meta name="viewport" content="width=device-width, initial-scale=1"> 7 <title>Title</title> 8 <script src="/static/jquery-3.2.1.js"></script> 9 </head> 10 <body> 11 <h1>JIAのHOMESITE</h1> 12 13 <script> 14 $(function () { 15 var url = 'http://127.0.0.1:8000/get_data.html/'; 16 $.ajax({ 17 url:url, 18 type:'GET', 19 dataType:'text', 20 success:function (data,statusText,xmlHttpRequest) { 21 console.log(data); 22 } 23 }) 24 }) 25 26 </script> 27 </body> 28 </html>?
b、支持跨域,復雜請求
對于復雜請求,會先發一次預檢(OPTIONS)請求,如果服務端允許,那么再發送一次正式請求(如PUT等,總之就是真正的請求)。
這個時候,我們需要在后端進行判斷,如果允許用戶獲取數據,那么當預檢(OPTIONS)過來的時候,我們需要返回允許訪問的請求。
?
- “預檢”請求時,允許請求方式則需服務器設置響應頭:Access-Control-Request-Method
- “預檢”請求時,允許請求頭則需服務器設置響應頭:Access-Control-Request-Headers
- “預檢”緩存時間,服務器設置響應頭:Access-Control-Max-Age
?
?服務端返回的數據
1 from django.shortcuts import render, HttpResponse 2 3 4 def get_data(request): 5 6 7 if request.method == 'OPTIONS': 8 response = HttpResponse() // 返回的內容可以為空,主要需要返回請求頭 9 response['Access-Control-Allow-Origin'] = '*' 10 response['Access-Control-Allow-Methods'] = 'PUT' // 允許的復雜請求方式為 PUT 請求; 11 response['Access-Control-Allow-Headers'] = 'k1' // 復雜請求還有一種情況就是定制請求頭,這種情況下,我們在返回的響應中應該設置該響應頭,代表允許發送請求頭的key是什么 12 return response 13 14 if request.method == 'PUT': 15 response = HttpResponse('機密文件') 16 response['Access-Control-Allow-Origin'] = '*' 17 return response?
?
?客戶端定義及獲取數據
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta http-equiv="x-ua-compatible" content="IE=edge"> 6 <meta name="viewport" content="width=device-width, initial-scale=1"> 7 <title>Title</title> 8 <script src="/static/jquery-3.2.1.js"></script> 9 </head> 10 <body> 11 <h1>JIAのHOMESITE</h1> 12 13 <script> 14 $(function () { 15 var url = 'http://127.0.0.1:8000/get_data.html/'; 16 $.ajax({ 17 url:url, 18 type:'PUT', 19 dataType:'text', 20 headers:{'k1':'v1'}, 21 success:function (data,statusText,xmlHttpRequest) { 22 console.log(data); 23 24 //獲取響應頭 25 console.log(xmlHttpRequest.getAllResponseHeaders()); //Content-Type: text/html; charset=utf-8 26 } 27 }) 28 }) 29 30 </script> 31 </body> 32 </html>?
如果我們不想每次都經過“預檢”這個環節的話,那么我們可以在服務器的響應頭中增加一組:Access-Control-Max-Age的響應頭,可以寫成
1 response['Access-Control-Allow-Headers'] = 10 // 默認單位為秒c、跨域獲取響應頭
默認獲取到的所有響應頭只有基本信息,如果想要獲取自定義的響應頭,則需要在服務器端設置Access-Control-Expose-Headers。
d、跨域傳輸cookie
在跨域請求中,默認情況下,HTTP Authentication信息,Cookie頭以及用戶的SSL證書無論在預檢請求中或是在實際請求都是不會被發送。
?
如果想要發送:
- 瀏覽器端:XMLHttpRequest的withCredentials為true
- 服務器端:Access-Control-Allow-Credentials為true
- 注意:服務器端響應的?Access-Control-Allow-Origin 不能是通配符 *
?
?服務端返回的數據
1 from django.shortcuts import render, HttpResponse 2 3 4 def get_data(request): 9 10 if request.method == 'OPTIONS': 11 response = HttpResponse() 12 response['Access-Control-Allow-Origin'] = 'http://127.0.0.1:8888' 13 response['Access-Control-Allow-Methods'] = 'PUT' 14 response['Access-Control-Allow-Headers'] = 'k1' 15 response['Access-Control-Allow-Credentials'] = 'true' #加在了這里 16 return response 17 18 if request.method == 'PUT': 19 response = HttpResponse('機密文件') 20 response['Access-Control-Allow-Origin'] = 'http://127.0.0.1:8888' 21 response['Access-Control-Allow-Credentials'] = 'true' #加在了這里 22 return response?
?客戶端定義及獲取數據
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta http-equiv="x-ua-compatible" content="IE=edge"> 6 <meta name="viewport" content="width=device-width, initial-scale=1"> 7 <title>Title</title> 8 <script src="/static/jquery-3.2.1.js"></script> 9 </head> 10 <body> 11 <h1>JIAのHOMESITE</h1> 12 13 <script> 14 $(function () { 15 var url = 'http://127.0.0.1:8000/get_data.html/'; 16 $.ajax({ 17 url:url, 18 type:'PUT', 19 dataType:'text', 20 headers:{'k1':'v1'}, 21 xhrFields:{withCredentials:true}, //加在了這里 22 success:function (data,statusText,xmlHttpRequest) { 23 console.log(data); 24 25 //獲取響應頭 26 console.log(xmlHttpRequest.getAllResponseHeaders()); 27 } 28 }) 29 }) 30 31 </script> 32 </body> 33 </html>?
參考博文:http://www.cnblogs.com/alwaysInMe/p/7686931.html
?
轉載于:https://www.cnblogs.com/metianzing/p/7868842.html
總結
- 上一篇: qt 自定义标题栏
- 下一篇: C# ConcurrentBag实现