處理跨域請求
瀏覽器具有同源策略,會禁止向與當前頁面不同的域發送請求,只要是協議,域名,端口中有任何一個不同,都被當作是不同的域,這雖然是一種保護數據的機制,但是對我們開發來說確是個麻煩,解決辦法有很多,這裏介紹一下JSONP和CORS
先來理解一下瀏覽器這個同源策略是什麽,就是瀏覽器禁止掉向其他域名發送的請求,其實是在請求回來的時候被禁掉的
哪些操作會受同源策略,哪些不會呢?
requests模塊不受影響,因為沒有經過瀏覽器
ajax發請求時,瀏覽器會限制(我們就是要解決這個問題)
有src屬性的都不受同源策略限制 -img,script,iframe
但是註意,script中的src,拿到的數據會當作js代碼
JSONP
jsonp是一種機智的方式,本質就是在遠程發送數據的時候,在數據外層套一個函數名,把數據以函數的形式返回(這樣才能被script識別)
在本地先定義一個函數,當遠程發送過來數據,本地就當成一個函數執行,真實數據就相當於參數
jsonp本質就是創建一個script標簽,把url放到src屬性中,就是相當於發送了一個get請求,事實上jsonp只能發送get請求
用一個簡單的示例來理解一下jsonp的本質
客戶端的地址是:localhost:8000,要向服務端 localhost:8888 發送請求,獲取數據,
利用jsonp來避開同源策略:
客戶端:
<!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><button onclick="jsonp(‘http://127.0.0.1:8888/get_data?callback=func‘)">獲取數據</button>{#通過參數向服務端發送函數名,這樣就能實現動態生成函數名#}
<script> function func(arg) { console.log(arg); document.head.removeChild(tag); } function jsonp(url) { tag = document.createElement(‘script‘);//創建一個script標簽,用來發送請求,註意是一個全局的變量,以便在func函數中刪除 tag.src = url; document.head.appendChild(tag); //在html的頭文件中添加這個script標簽 } </script> </body> </html>
服務端:
from django.shortcuts import HttpResponse
def get_data(request):
func_name = request.GET.get(‘callback‘)#從請求頭中獲取函數名
return HttpResponse(‘%s(數據)‘%func_name)#返回一個函數,把數據當作參數
1.jsonp就是利用script的src發送請求不會被受同源策略限制,然後通過src屬性向服務端發送請求
2.script會把請求來的數據當成是js代碼,所以服務端返回的數據不能直接是數據,會報錯
3.把返回值封裝成一個函數的形式,真實數據放在函數的參數中,客戶端等收到後,從函數中拿到數據就行了
4.這個函數名最好是動態生成的,不然每次發送一個請求就要前後端商量好函數名,太麻煩
以上就是jsonp的原理
所以使用jsonp,本地需要定義一個函數,而遠程需要把數據封裝成一個函數
再來看看ajax如何實現跨域請求
<!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> <button onclick="jsonp()">獲取數據</button> <script> function func(arg) { console.log(arg); document.head.removeChild(tag); } {# 這種實際上就是內部幫我們實現創建一個script標簽#} function jsonp() { $.ajax({ url:‘http://127.0.0.1/get_data‘, type:‘GET‘, dataType:‘JSONP‘, jsonp:‘callback‘, jsonCallback:‘func‘{# 這兩行就是自動在url後面添加?callback=func #} }) } </script>
jsonp只能發送get請求,但是可不是所有的數據都能封裝在參數中,想要發送post請求,還得用CORS
CORS跨站資源共享(Cross-Origin Resource Sharing)
再來看一下跨域請求時,瀏覽器的提示信息:
之所以ajax發送請求,返回的數據拿不到,是因為少了一點東西。提示缺少一個響應頭,所以CORS的原理就是添加上這個響應頭
只需要修改視圖函數
from django.shortcuts import HttpResponse def get_data(request): response = HttpResponse(‘數據‘) response[‘Access-Control-Allow-Origin‘] = ‘http://127.0.0.1:8888‘#這樣表示允許這個地址訪問 #response[‘Access-Control-Allow-Origin‘] = ‘*‘#這樣表示允許所有地址訪問 return response
這樣就可以了,這是最簡單的情況,本地不用做任何事,遠程設置一個響應頭
還有一種復雜點的情況:非簡單請求
簡單請求&非簡單請求:
簡單請求的條件:
1.請求方式為 HEAD,GET,POST
2.請求頭中Content-Type的值是下面這三個中的一個
application/x-www-form-urlencoded
multipart/form-data
text/plain
同時滿足以上兩個條件時,才是簡單請求,有一個不滿足就是復雜請求
對於復雜請求,以PUT請求為例,會先發一個options請求,用來預檢,對於預檢請求,也要返回響應頭
遠程視圖函數要這樣處理:
def data(request): if request.method == ‘OPTIONS‘: #預檢 response = HttpResponse() response[‘Access-Control-Allow-Origin‘] = ‘*‘ response[‘Access-Control-Allow-Methods‘] = ‘PUT‘#允許這個請求頭 return response elif request.method == ‘PUT‘: #預檢通過後真正的請求 response = HttpResponse(‘數據‘) response[‘Access-Control-Allow-Origin‘] = ‘*‘ return response
如果GET請求自定義請求頭,也變成了復雜請求,也需要設置一下,允許這個請求頭
比如自定義了一個請求頭:xxx:yyyy
那遠程代碼中就要允許這個請求頭
def data(request): if request.method == ‘OPTIONS‘: #預檢 response = HttpResponse() response[‘Access-Control-Allow-Origin‘] = ‘*‘ # response[‘Access-Control-Allow-Methods‘] = ‘PUT‘ response[‘Access-Control-Allow-Headers‘] = ‘xxx‘#允許這個請求頭 return response elif request.method == ‘PUT‘: #預檢通過後真正的請求 response = HttpResponse(‘數據‘) response[‘Access-Control-Allow-Origin‘] = ‘*‘ return response雖然復雜請求可以解決 但是,要盡量避免復雜請求,會增加服務器壓力
總結,解決跨域請求,常用的有三種方式:requests模塊,cors,jsonp 後兩種直接從前端拿數據,不需要經過服務器,requests模塊是先把請求發送到服務器,服務器向目標發送請求,再返回數據到本地 jsonp和cors相比,兼容性更好一點 參考博客:http://www.cnblogs.com/wupeiqi/articles/5703697.html
處理跨域請求