先後端分離djangorestframework——解決跨域請求

跨域

什麼是跨域

好比一個連接:http://www.baidu.com(端口默認是80端口),html

若是再來一個連接是這樣:http://api.baidu.com,這個就算是跨域了(由於域名不一樣)前端

再來一個:https://www.baidu.com,這個也是跨域了(由於協議不一樣,用的https)vue

再來一個http://www.baidu.com:8888,這個也算跨域,端口號不一樣python

 

舉個實際的例子:ios

  • API接口數據部署在baidu.com上;
  • Ajax文件部署在cnblogs.com上,Ajax文件會向API (baidu.com) 發送請求,返回數據;
  • 用戶經過bing.com訪問cnblogs.com的Ajax文件,請求數據

 

以上過程就發生了跨域訪問。若是直接使用Ajax來請求就會失敗,就像Chrome提示的:git

No 'Access-Control-Allow-Origin' header is present on the requested resource.github

 

跨域直白點就是,請求協議,域名(IP),端口號,三個其中任何一個不一樣都算跨域npm

CORS

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頭信息不超出如下幾種字段:

  • Accept
  • Accept-Language
  • Content-Language
  • Last-Event-ID
  • Content-Type

並且Content-Type只能是下列類型:

  • application/x-www-from-urlencoded
  • multipart/form-data
  • text/plain

複雜請求

除了簡單請求的都是複雜請求了,因此若是傳輸json數據,Content-Type類型就是json了,因此必定是複雜請求了

複雜請求會先發出一個預請求,又叫預檢,OPTIONS請求

 

常見的解決跨域的方法

1.協議和域名,端口都是用同一個就行

2.利用jsonp解決跨域問題

 由於看代碼相信你也看到了,那個script腳本標籤導入的兩個js文件,一個用的vue,一個用的axios,而後用到的是各自的cdn,url也都不同:

 

其實也算跨域了,可是瀏覽器並無給咱們攔截,因此咱們是否也能夠直接用script的src屬性來訪問,固然能夠:

 

刷新剛纔那個前端客戶端頁面,發現真的能夠這樣

那麼再大膽一點,發一個函數名怎麼樣?而後前端先定義好這個函數名,試試看:

 

後端的視圖函數裏返回這個函數調用的字符串:

 

前端訪問,可行,確實打印了

 

可是,這隻能作GET啊,若是作POST,PUT啥的請求呢?就沒辦法了,並且在調用函數時,前端和後端必須商量好函數名,必須統一才行,因此這jsonp仍是有很大的弊端的

 

2.修改Django中的views.py文件

 


修改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

  

 

4.在後端作處理——Python是利用django中間件添加響應頭 

 

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的方法就能夠完美解決火狐瀏覽器問題

 

5.使用第三方庫django-cors-headers

 

在Django中,有人開發了CORS-header的middleware,github:傳送門 

只需在settings.py中作一些簡單的配置便可,其餘不用做任何修改,咱們也不用本身手動的建立中間件對response處理了,直接用如下配置便可,  如今用起來服務器端徹底開放,開啓CORS,沒有任何跨域煩惱 


安裝django-cors-headers:

pip install django-cors-headers

  

在settings.py中增長:

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得不行
相關文章
相關標籤/搜索