Django_跨域請求處理
同源策略(Same origin policy)
是一種約定安全策略,瀏覽器自帶的安全功能
Web是構建在同源策略基礎之上的,瀏覽器只是針對同源策略的一種實現
同源 : 域名 , 協議 , 埠 策略 : 當一個瀏覽器的兩個tab頁中分別開啟來 即檢查是否同源,只有同源的指令碼才會被執行。 如果非同源,那麼在請求資料時,瀏覽器會在控制檯中報一個異常,提示拒絕訪問。
簡單的跨域請求測試
場景:
新建兩個專案 obj1 和 obj2 ,兩個專案分別 埠號為 8006 和 8008
檢視傳回資料分別為 "123456" 和 "654321"
obj1.views.py
from django.shortcuts import render,HttpResponse # Create your views here. def index(request): return render(request,"index.html") def service(request): return HttpResponse("123456")obj2.views.py
from django.shortcuts import render,HttpResponse # Create your views here. def index(request): return render(request,"index.html") def service(request):
print("654321") return HttpResponse("654321")
驗證方式:
通過 obj1中的 index 頁面通過 button 按鈕標籤繫結 ajax事件 的 url 向 obj2 的 "http://127.0.0.1:8008/service/" 路徑請求資料
$(".get_service").click(function () { $.ajax({ url:"http://127.0.0.1:8008/service/", success:function (data) { console.log(data) } }) })
驗證結果:
無法取出,同源策略確實無法允許跨域的請求
通過 obj2 的後端列印確實可以看到函式被執行了,說明攔截髮生在前端瀏覽器進行的操作
基於json 的跨域請求方式
如果所有的跨域請求拿不到,那是怎麼匯入外部的 js ,bootstrap ,之類的檔案呢。
很明顯 script 標籤的 src 屬性是不被攔截的
在同源策略中也只限定了指令碼的執行,對於標籤src 屬性並沒有干涉
因此基於 script 標籤的 src 屬性可以做些手腳
簡單的測試1階段:
測試目的:
檢視script 標籤的 src 屬性請求的可行性
測試分析:
便於測試我們將 http://127.0.0.1:8008/service/ 返回的資料更改一下
obj2.views.py
def service(request): print("yangtuo") return HttpResponse("yangtuo")測試結果 : script 標籤的 src 屬性請求可以實現跨域請求
同上述一樣 obj2 後臺執行
但是前端報錯是 變數名沒有被宣告,最起碼證明了一點資料確實被傳過來了
簡單的測試1階段總結:
驗證 成功,scirpt 的 src 屬性可以通過向目的url 請求並拿回資料放在 標籤內容 裡面
簡單的測試2階段:
測試目的:
在證實了script 標籤的 src 屬性請求可以實現跨域請求後,
進一步探索傳遞資料的可行性
測試分析:
既然拿到的資料是變數名的形式無宣告報錯,那解決途徑兩種
1. 不讓傳回變數名 :瀏覽器對標籤內容的處理必然是 去除 "" 雙引號,繞不過去。無解
2. 宣告變數 : 那就提前宣告一個變數
obj1. index.html 在頁面宣告一個和請求資料相同的變數
<script> var yangtuo </script>測試結果 : 解決報錯問題
但是並沒有什麼卵用:
對每次請求的資料設計出變數那我除非提前知道我要請求什麼。我都知道了我還請求個p
延伸:
既然傳過來的是個變數。如果這個變數是個方法我加個"()" 不就可以執行了?
方法比單純的資料變數可做的事情就多了去了!
簡單的測試3階段:
測試目的:
利用宣告方法的方式然後通過傳入的引數拿到想要的資料
測試分析:
obj2 views.py 真正傳遞的資料通過預先一致的方法中的引數中傳遞
def service(request): print("yangtuo") data = "123" return HttpResponse(f"yangtuo({data})")obj1 index.html 通過預先一致的方法的引數執行取出來目標資料
<script> function yangtuo(arg) { alert(arg) } </script>
測試結果 :
拿到了預期的資料並可以進行相應的操作 ,基本實現了我們的預期要求
延伸:
對於日常處理的資料在網路間傳輸必然是json的格式,因此基於 jsonp 的跨域請求在這一基礎上誕生
簡單的測試4階段:
測試目的:
測試傳遞更復雜的資料
測試分析:
obj2 views.py 這次嘗試一下傳遞字典,提前用json 處理成字串形式
def service(request): print("yangtuo") data = {"name": "yangtuo", "age": 18} data = json.dumps(data) return HttpResponse(f"yangtuo({data})")
obj1 index.html 檢視傳過來的資料型別並且使用一下資料看看
<script> function yangtuo(arg) { console.log(arg); console.log(typeof arg); var data = JSON.parse(arg); console.log(data); console.log(typeof data); } </script>測試結果 :
什麼鬼。不應該傳過來是一個字串嗎?怎麼直接是物件了。
延伸 :
經查閱,新版本的js 中已經自動對資料進行了還原。不再需要自己還原資料了。所以測試結果是成功的。
但是這種拿資料的方法觸發是 script 標籤的 src 請求,執行必須要走整個頁面的重新整理,實在有點蠢
我們期待的是類似於 ajax 的請求方式來拿到資料
簡單的測試5階段:
測試目的:
實現AJAX方式的請求方式的跨域請求
測試分析:
html 的標籤的在建立的時候自動會被渲染執行,
因此可以用ajax 的方式建立 script 標籤從而控制執行時間
obj1 index.html 繫結一個點選事件,建立一個 src 屬性為跨域請求的 script 標籤
<script> $(".get_service").click(function () { var ele_script = $("<script>"); // 建立標籤 ele_script.attr("src","http://127.0.0.1:8008/service/"); // 新增標籤屬性 $("body").append(ele_script) // 新增標籤 }) </script>
測試結果 :成功
延伸:
完善1 :
存在一個問題。如果你點選了標籤不消除的話標籤會一直在,
畢竟我們用完這標籤就沒用了,因此還需要將它消除掉,不然用多了全都是這標籤了‘
完善後的程式碼
<script> $(".get_service").click(function () { var ele_script = $("<script>"); // 建立標籤 ele_script.attr("src","http://127.0.0.1:8008/service/"); // 新增標籤屬性 ele_script.attr("id","jsonp"); // 新增標籤屬性 $("body").append(ele_script); // 新增標籤 $("#jsonp").remove() // 刪除標籤 }) </script>完善2:
畢竟不是隻有一個數據要取,每個標籤都繫結事件寫這麼多很蠢
封裝成函式在每個要取的時候呼叫即可
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <title>Title</title> <meta name="viewport" content="width=device-width, initial-scale=1"> </head> <body> <h3>INDEX</h3> {#資料處理觸發器#} <button class="get_service">啦啦啦你來點我啊~</button> <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script> <script> // 對資料的操作 function yangtuo(arg) { console.log(arg); console.log(typeof arg); } // 定義一個專門取資料的 方法 function get_jsonp_data(url) { var ele_script = $("<script>"); // 建立標籤 ele_script.attr("src",url); // 新增標籤屬性 ele_script.attr("id","jsonp"); // 新增標籤屬性 $("body").append(ele_script); // 新增標籤 $("#jsonp").remove() } </script> <script> // 點選事件 $(".get_service").click(function () { get_jsonp_data("http://127.0.0.1:8008/service/") }) </script> </body> </html>
完善3 :
說了這麼多的前提必須是兩端的資料必須基於一個協商好的方法名字。
可不可以有什麼放伺服器知道我的方法然後基於方法直接給資料呢?
利用 request 裡面的 get 請求傳遞方法名字即可,伺服器基於拿到的名字來返回方法名執行
obj1 index.html
<script> // 點選事件 $(".get_service").click(function () { get_jsonp_data("http://127.0.0.1:8008/service/?callbacks=yangtuo") }) </script>obj2 views.py
def service(request): func = request.GET["callbacks"] # 獲取請求者提供的方法名 data = {"name": "yangtuo", "age": 18} data = json.dumps(data) return HttpResponse(f"{func}({data})")
以上都是簡單的測試
以下為最終版本!
原理就是基於 上面的測試
用 get 請求(必須是get)發生請求同時帶一個隨機生成的引數。
服務端無視引數直接資料處理方法,在請求端再呼叫方法處理資料
$(".get_service").click(function () { $.ajax({ url: "http://127.0.0.1:8008/service/", type: "get", dataType: "jsonp", // 偽造ajax 基於script jsonp: 'callbacks', // 建立一個 get請求的引數的 鍵 //jsonpCallback:"alex", // 建立一個 get 請求引數的 值 success: function (data) { // 如果使用 jsonCallback引數 可以用直接在下面寫自己的資料處理過程 console.log(data) } }) });
def service(request): func = request.GET["callbacks"] # 獲取請求者提供的方法名 data = {"name": "yangtuo", "age": 18} data = json.dumps(data) return HttpResponse(f"{func}({data})") # 關於方法名是什麼無所謂,至少中間值而已
基於 cors 的跨域請求
def service(request): # func = request.GET["callbacks"] # 獲取請求者提供的方法名 # data = {"name": "yangtuo", "age": 18} # data = json.dumps(data) # return HttpResponse(f"{func}({data})") # 關於方法名是什麼無所謂,至少中間值而已 info={"name":"egon","age":34,"price":200} response=HttpResponse(json.dumps(info)) response["Access-Control-Allow-Origin"]="http://127.0.0.1:8006" # response["Access-Control-Allow-Origin"]="*" return response