1. 程式人生 > >跨域請求相關

跨域請求相關

success image attr ajax請求 會有 分享圖片 list tle 項目

說到跨域請求,我們要先了解為什麽需要跨域請求以及跨域請求的問題

隨著前後端分離的浪潮來臨,越來越多的項目采用前後端分離的模式構建,從之前的一個服務器,到前後端各自的服務器,所以不同服務器就會出現數據的交互,就出現了跨域請求。

說到跨域的問題,就要提到瀏覽器的同源策源。

同源策略

同源策略(Same origin policy)是一種約定,它是瀏覽器最核心也最基本的安全功能,如果缺少了同源策略,則瀏覽器的正常功能可能都會受到影響。可以說Web是構建在同源策略基礎之上的,瀏覽器只是針對同源策略的一種實現。

同源策略,它是由Netscape提出的一個著名的安全策略。現在所有支持JavaScript 的瀏覽器都會使用這個策略。所謂同源是指,域名,協議,端口相同
。當一個瀏覽器的兩個tab頁中分別打開來 百度和谷歌的頁面當瀏覽器的百度tab頁執行一個腳本的時候會檢查這個腳本是屬於哪個頁面的,即檢查是否同源,只有和百度同源的腳本才會被執行。如果非同源,那麽在請求數據時,瀏覽器會在控制臺中報一個異常,提示拒絕訪問。

示例:

項目1: 技術分享圖片 技術分享圖片
==================================http://127.0.0.1:8001項目的index
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="http://code.jquery.com/jquery-latest.js"></script>
</head>
<body>


<button>ajax</button>
{% csrf_token %}

<script>
    $("button").click(function(){


        $.ajax({
            url:"http://127.0.0.1:7766/SendAjax/",
            type:"POST",
            data:{"username":"yuan","csrfmiddlewaretoken":$("[name=‘csrfmiddlewaretoken‘]").val()},
            success:function(data){
                alert(123);
                alert(data)
            }
        })
    })
</script>
</body>
</html>


==================================http://127.0.0.1:8001項目的views

def index(request):


    return render(request,"index.html")


def ajax(request):
    import json
    print(request.POST,"+++++++++++")
    return HttpResponse(json.dumps("hello"))
技術分享圖片

項目2:

技術分享圖片 技術分享圖片
==================================http://127.0.0.1:8002項目的index
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="http://code.jquery.com/jquery-latest.js"></script>
</head>
<body>


<button>sendAjax</button>

{% csrf_token %}

<script>
    $("button").click(function(){


        $.ajax({
            url:"/SendAjax/",
            type:"POST",
            data:{"username":"yuan","csrfmiddlewaretoken":$("[name=‘csrfmiddlewaretoken‘]").val()},
            success:function(data){
                alert(data)
            }
        })
    })
</script>

</body>
</html>


==================================http://127.0.0.1:8002項目的views

def index(request):

    return render(request,"index.html")

from django.views.decorators.csrf import csrf_exempt

@csrf_exempt


def SendAjax(request):

    import json

    print("++++++++")

    return HttpResponse(json.dumps("hello2"))
技術分享圖片

當點擊項目1的按鈕時,發送了請求,但是會發現報錯如下:

技術分享圖片

已攔截跨源請求:同源策略禁止讀取位於 http://127.0.0.1:7766/SendAjax/ 的遠程資源。(原因:CORS 頭缺少 Access-Control-Allow-Origin‘)。

註意:跨域發送的請求執行了,並返回了對應的數據,但是瀏覽器會對非同源請求做攔截

Jsonp

jsonp是json用來跨域的一個東西。原理是通過script標簽的跨域特性來繞過同源策略。

思考:這算怎麽回事?

1 <script src="http://code.jquery.com/jquery-latest.js"></script>

借助script標簽,實現跨域請求,示例:

技術分享圖片
# =============================http://127.0.0.1:8001/index


<button>ajax</button>
{% csrf_token %}

<script>
    function func(name){
        alert(name)
    }
</script>

<script src="http://127.0.0.1:7766/SendAjax/"></script>


# =============================http://127.0.0.1:8002/
from django.views.decorators.csrf import csrf_exempt

@csrf_exempt


def SendAjax(request):

    import json

    print("++++++++")
    # dic={"k1":"v1"}
    return HttpResponse("func(‘yuan‘)")  # return HttpResponse("func(‘%s‘)"%json.dumps(dic))
技術分享圖片

這其實就是JSONP的簡單實現模式,或者說是JSONP的原型:創建一個回調函數,然後在遠程服務上調用這個函數並且將JSON 數據形式作為參數傳遞,完成回調。

將JSON數據填充進回調函數,這就是JSONP的JSON+Padding的含義。

一般情況下,我們希望這個script標簽能夠動態的調用,而不是像上面因為固定在html裏面所以沒等頁面顯示就執行了,很不靈活。我們可以通過javascript動態的創建script標簽,這樣我們就可以靈活調用遠程服務了。

技術分享圖片
<button onclick="f()">sendAjax</button>

<script>
    function addScriptTag(src){
         var script = document.createElement(script);
         script.setAttribute("type","text/javascript");
         script.src = src;
         document.body.appendChild(script);
         document.body.removeChild(script);
    }


    function func(name){
        alert("hello"+name)
    }

    function f(){
         addScriptTag("http://127.0.0.1:7766/SendAjax/")
    }
</script>
技術分享圖片

為了更加靈活,現在將你自己在客戶端定義的回調函數的函數名傳送給服務端,服務端則會返回以你定義的回調函數名的方法,將獲取的json數據傳入這個方法完成回調:

將8001的f()改寫為:

1 2 3 function f(){ addScriptTag("http://127.0.0.1:7766/SendAjax/?callbacks=func") }

8002的views改為:

1 2 3 4 5 6 7 8 9 10 def SendAjax(request): import json dic={"k1":"v1"} print("callbacks:",request.GET.get("callbacks")) callbacks=request.GET.get("callbacks") return HttpResponse("%s(‘%s‘)"%(callbacks,json.dumps(dic)))

jQuery對JSONP的實現

getJSON

jQuery框架也當然支持JSONP,可以使用$.getJSON(url,[data],[callback])方法

8001的html改為:

技術分享圖片
<button onclick="f()">sendAjax</button>

<script>

    function f(){
          $.getJSON("http://127.0.0.1:7766/SendAjax/?callbacks=?",function(arg){
            alert("hello"+arg)
        });
    }
    
</script>
技術分享圖片

8002的views不改動。

結果是一樣的,要註意的是在url的後面必須添加一個callback參數,這樣getJSON方法才會知道是用JSONP方式去訪問服務,callback後面的那個問號是內部自動生成的一個回調函數名。

此外,如果說我們想指定自己的回調函數名,或者說服務上規定了固定回調函數名該怎麽辦呢?我們可以使用$.ajax方法來實現

$.ajax

8001的html改為:

技術分享圖片
<script>

    function f(){
          $.ajax({
                url:"http://127.0.0.1:7766/SendAjax/",
                dataType:"jsonp",
                jsonp: callbacks,
                jsonpCallback:"SayHi"
           });

       }

    function SayHi(arg){
                alert(arg);
            }

</script>
技術分享圖片

8002的views不改動。

當然,最簡單的形式還是通過回調函數來處理:

技術分享圖片
<script>

    function f(){

            $.ajax({
               url:"http://127.0.0.1:7766/SendAjax/",
               dataType:"jsonp",            //必須有,告訴server,這次訪問要的是一個jsonp的結果。
               jsonp: callbacks,          //jQuery幫助隨機生成的:callbacks="wner"
               success:function(data){
                   alert("hi "+data)
              }
         });

       }

</script>
技術分享圖片

jsonp: ‘callbacks‘就是定義一個存放回調函數的鍵,jsonpCallback是前端定義好的回調函數方法名‘SayHi‘,server端接受callback鍵對應值後就可以在其中填充數據打包返回了;

jsonpCallback參數可以不定義,jquery會自動定義一個隨機名發過去,那前端就得用回調函數來處理對應數據了。利用jQuery可以很方便的實現JSONP來進行跨域訪問。  

註意 JSONP一定是GET請求

應用

技術分享圖片
<input type="button" onclick="AjaxRequest()" value="跨域Ajax" />


<div id="container"></div>


    <script type="text/javascript">
        function AjaxRequest() {
            $.ajax({
                url: http://www.jxntv.cn/data/jmd-jxtv2.html?callback=list&_=1454376870403,
                type: GET,
                dataType: jsonp,
                jsonp: callback,
                jsonpCallback: list,
                success: function (data) {
                    
                    $.each(data.data,function(i){
                        var item = data.data[i];
                        var str = "<p>"+ item.week +"</p>";
                        $(#container).append(str);
                        $.each(item.list,function(j){
                            var temp = "<a href=‘" + item.list[j].link +"‘>" + item.list[j].name +" </a><br/>";
                            $(#container).append(temp);
                        });
                        $(#container).append("<hr/>");
                    })

                }
            });
        }
</script>
View Code

CORS

一、簡介

CORS需要瀏覽器和服務器同時支持。目前,所有瀏覽器都支持該功能,IE瀏覽器不能低於IE10。

整個CORS通信過程,都是瀏覽器自動完成,不需要用戶參與。對於開發者來說,CORS通信與同源的AJAX通信沒有差別,代碼完全一樣。瀏覽器一旦發現AJAX請求跨源,就會自動添加一些附加的頭信息,有時還會多出一次附加的請求,但用戶不會有感覺。

因此,實現CORS通信的關鍵是服務器。只要服務器實現了CORS接口,就可以跨源通信。

二、兩種請求

瀏覽器將CORS請求分成兩類:簡單請求(simple request)和非簡單請求(not-so-simple request)。

只要同時滿足以下兩大條件,就屬於簡單請求。

技術分享圖片
(1) 請求方法是以下三種方法之一:
HEAD
GET
POST
(2)HTTP的頭信息不超出以下幾種字段:
Accept
Accept-Language
Content-Language
Last-Event-ID
Content-Type:只限於三個值application/x-www-form-urlencoded、multipart/form-data、text/plain
技術分享圖片

凡是不同時滿足上面兩個條件,就屬於非簡單請求。

瀏覽器對這兩種請求的處理,是不一樣的。

技術分享圖片
* 簡單請求和非簡單請求的區別?

   簡單請求:一次請求
   非簡單請求:兩次請求,在發送數據之前會先發一次請求用於做“預檢”,只有“預檢”通過後才再發送一次請求用於數據傳輸。

* 關於“預檢” - 請求方式:OPTIONS - “預檢”其實做檢查,檢查如果通過則允許傳輸數據,檢查不通過則不再發送真正想要發送的消息 - 如何“預檢” => 如果復雜請求是PUT等請求,則服務端需要設置允許某請求,否則“預檢”不通過 Access-Control-Request-Method => 如果復雜請求設置了請求頭,則服務端需要設置允許某請求頭,否則“預檢”不通過 Access-Control-Request-Headers
技術分享圖片

支持跨域,簡單請求

服務器設置響應頭:Access-Control-Allow-Origin = ‘域名‘ 或 ‘*‘

備註:“*”表示允許所有跨域請求

支持跨域,復雜請求

由於復雜請求時,首先會發送“預檢”請求,如果“預檢”成功,則發送真實數據。

  • “預檢”請求時,允許請求方式則需服務器設置響應頭:Access-Control-Allow-Methods
  • “預檢”請求時,允許請求頭則需服務器設置響應頭:Access-Control-Allow-Headers
  備註:設置請求方式時,值必須是大寫的請求方法。   不論是設置請求頭還是請求方式,對個數值之間用","分割
response[Access-Control-Allow-Methods] = DELETE,PUT
response[Access-Control-Allow-Headers] = 請求頭中允許的key1,key2,Content-Type‘  # 允許其他形式的數據,比如json

預檢請求方式時,會報錯:以put請求為例:

技術分享圖片

預檢請求頭時,會報錯:

技術分享圖片

允許跨域時,最好是寫一個中間件,將所有的響應中加上允許的頭。
from django.utils.deprecation import MiddlewareMixin


class CrossMiddleware(MiddlewareMixin):

    def process_response(self,request,response):
        ret = response
        ret[Access-Control-Allow-Methods] = DELETE,PUT
        ret[Access-Control-Allow-Origin] = *
     if request.method == ‘OPTIONS‘:
      response[Access-Control-Allow-Headers‘] = ‘Content-Type‘
return ret

跨域請求相關