好比一個連接:http://www.baidu.com(端口默認是80端口),html
若是再來一個連接是這樣:http://api.baidu.com,這個就算是跨域了(由於域名不一樣)前端
再來一個:https://www.baidu.com,這個也是跨域了(由於協議不一樣,用的https)vue
再來一個http://www.baidu.com:8888,這個也算跨域,端口號不一樣python
舉個實際的例子:ios
以上過程就發生了跨域訪問。若是直接使用Ajax來請求就會失敗,就像Chrome提示的:git
No 'Access-Control-Allow-Origin' header is present on the requested resource.github
跨域直白點就是,請求協議,域名(IP),端口號,三個其中任何一個不一樣都算跨域npm
CORS(跨域資源共享,Cross-Origin Resource Sharing)是一種跨域訪問的機制,可讓Ajax實現跨域訪問 django
那麼若是跨域是什麼樣的呢?好比,我這裏有個restframework項目做爲服務器,項目名爲drfversion,測試的app名叫testapp,url以下:json
view:
啓動項目,直接訪問這個API接口測試:
在django的templates目錄下新建一個test.html,簡單的創建了一個vue和axios的項目,做爲前端的異步請求,寫入如下參數:
點那個谷歌瀏覽器圖標,而後直接谷歌瀏覽器打開,這裏這個功能是pycharm給咱們提供的功能,此時這個做爲客戶端,訪問:
裏面的這個參數提示就是跨域了,瀏覽器默認有個同源策略,他檢測到這個http://localhost:63342的url與咱們後端啓動的DRF項目url:http://127.0.0.1:8000/test/不一樣源,也就是跨域了,因此報錯。
再看接口,看其實後端沒有錯誤,已經給咱們返回了數據:
因此跨域請求的根本緣由是瀏覽器的同源策略形成的,要解決這個跨域請求就能夠在這方面研究了
而跨域分簡單請求和複雜請求
HTTP方法是隻能是HEAD, GET和POST
HTTP頭信息不超出如下幾種字段:
並且Content-Type只能是下列類型:
除了簡單請求的都是複雜請求了,因此若是傳輸json數據,Content-Type類型就是json了,因此必定是複雜請求了
複雜請求會先發出一個預請求,又叫預檢,OPTIONS請求
由於看代碼相信你也看到了,那個script腳本標籤導入的兩個js文件,一個用的vue,一個用的axios,而後用到的是各自的cdn,url也都不同:
其實也算跨域了,可是瀏覽器並無給咱們攔截,因此咱們是否也能夠直接用script的src屬性來訪問,固然能夠:
刷新剛纔那個前端客戶端頁面,發現真的能夠這樣
那麼再大膽一點,發一個函數名怎麼樣?而後前端先定義好這個函數名,試試看:
後端的視圖函數裏返回這個函數調用的字符串:
前端訪問,可行,確實打印了
可是,這隻能作GET啊,若是作POST,PUT啥的請求呢?就沒辦法了,並且在調用函數時,前端和後端必須商量好函數名,必須統一才行,因此這jsonp仍是有很大的弊端的
修改views.py中對應API的實現函數,容許其餘域經過Ajax請求數據:
def myview(_request): response = HttpResponse(json.dumps({"key": "value", "key2": "value"})) response["Access-Control-Allow-Origin"] = "*" response["Access-Control-Allow-Methods"] = "POST, GET, OPTIONS" response["Access-Control-Max-Age"] = "1000" response["Access-Control-Allow-Headers"] = "*" return response
GET:
如今什麼都不作,只是get請求,再看下提示的是什麼:
注意這句話:No 'Access-Control-Allow-Origin' header is present on the requested resource.
好,在後端寫一箇中間件:
配置文件裏應用上:
重啓項目,前端再次訪問,此次終於沒有紅色的報錯了
POST:
再來個POST請求看看:
後端的view:
其餘後端代碼不變,前端訪問,又變紅了
注意這句話:Request header field Content-Type is not allowed by Access-Control-Allow-Headers in preflight response.
好的中間件作以下修改:
重啓訪問,有了:
OPTIONS(PUT,DELETE):
好若是是put或者delete請求呢?這個按前面說的簡單請求與複雜請求的說法,這兩個請求方式確定是複雜請求了,即那個OPTIONS了,搞一個put的請求看看,仍是先看看它報什麼錯:
view:
重啓訪問,報錯提示的字段跟前面的POST請求字段好像是一個說法對吧
可是咱們只加了post的,並沒加put或者delete的,修改中間件:
重啓訪問,確實已解決
而後,其實中間件仍是稍微優化一下,把簡單請求和複雜請求作個判斷處理,複雜請求能夠預檢嘛,節省下資源:
重啓訪問,沒有任何問題:
相關代碼:
中間件middle:
from django.utils.deprecation import MiddlewareMixin class CorsMiddle(MiddlewareMixin): def process_response(self, request, response): response['Access-Control-Allow-Origin'] = '*' if request.method == 'OPTIONS': response['Access-Control-Allow-Headers'] = 'Content-Type' response['Access-Control-Allow-Methods'] = 'PUT,DELETE' return response
view:
from rest_framework.views import APIView from rest_framework.views import Response class TestView(APIView): def get(self, request): return Response('跨域測試') def post(self, request): return Response('post接口測試') def put(self,request): return Response('put請求測試')
....(還有個delete請求就省略了,跟put請求同樣的)
settings:
MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', 'middle.CorsMiddle', ]
前端代碼test.html:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>test</title> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <script src="https://unpkg.com/axios/dist/axios.min.js"></script> <script> // function PrintTest() { // console.log('jsonp test'); // } </script> <script src="http://127.0.0.1:8000/test/"></script> </head> <body> <div id="app"></div> <script> const app = new Vue({ el: '#app', mounted() { axios.request({ url: 'http://127.0.0.1:8000/test/', method: 'GET',// "POST","PUT","DELETE" //data:{ // "name":"jack", //} }).then(function (data) { console.log(data) }) } }) </script> </body> </html>
以上4個方法,已經能夠解決大部分的跨域請求問題,可是因爲我後期再次遇到過更強大的問題:以上4個方法針對火狐瀏覽器無法,火狐瀏覽器安全性比較高,同樣會報跨域請求,即便你在django的中間件添加了response處理
下面這個使用第三方庫django-cors-headers的方法就能夠完美解決火狐瀏覽器問題
在Django中,有人開發了CORS-header的middleware,github:傳送門
只需在settings.py中作一些簡單的配置便可,其餘不用做任何修改,咱們也不用本身手動的建立中間件對response處理了,直接用如下配置便可, 如今用起來服務器端徹底開放,開啓CORS,沒有任何跨域煩惱
pip install django-cors-headers
INSTALLED_APPS = ( ... 'corsheaders', ... ) ... MIDDLEWARE_CLASSES = ( ... 'corsheaders.middleware.CorsMiddleware', # 注意順序,必須放在Comon前面 'django.middleware.common.CommonMiddleware', ... )
#如下直接賦值放在settings.py裏便可解決 CORS_ALLOW_CREDENTIALS = True CORS_ORIGIN_ALLOW_ALL = True CORS_ORIGIN_WHITELIST = ( '*' ) CORS_ALLOW_METHODS = ( 'DELETE', 'GET', 'OPTIONS', 'PATCH', 'POST', 'PUT', 'VIEW', ) CORS_ALLOW_HEADERS = ( 'XMLHttpRequest', 'X_FILENAME', 'accept-encoding', 'authorization', 'content-type', 'dnt', 'origin', 'user-agent', 'x-csrftoken', 'x-requested-with', 'Pragma', )
- 針對跨域請求,其實本質上是瀏覽器對返回的結果response的攔截
- 最多見的解決辦法就是在後端返回結果response時作數據處理,讓瀏覽器不攔截
- 大殺招使用django-cors-headers庫,秒殺一切,簡直6得不行