1. 程式人生 > >AJAX跨域請求和CORS跨域資源共享

AJAX跨域請求和CORS跨域資源共享

同源策略限制:

同源策略(Same origin policy)是一種約定,它是瀏覽器最核心也最基本的安全功能,如果沒有同源策略,攻擊者可以通過JavaScript獲取你的郵件以及其他敏感資訊,比如說閱讀私密郵件,傳送虛假郵件,檢視聊天記錄等等。所謂同源是指,協議,域名,埠相同。三者只有有一個不相同,就認為不同源!

跨域請求:

1. 對於主域相同而子域不同的例子,可以通過設定document.domain的辦法來解決。

ajax不允許跨子域請求,但是iframe可以!可以通過提升域的方法來實現。

例如www.a.com/a.html和a.com/b.html為例,只需在a.html中新增一個b.html的iframe,並且設定兩個頁面的document.domain都為’a.com’(只能為主域名)

,兩個頁面之間即可互相訪問了。

如果b.html要訪問a.html,可在子視窗(iframe)中通過window.parent來訪問父視窗的window物件。

例如www.a.com/a.html中的script:

document.domain='a.com';//提升域
varifr = document.createElement('iframe');
ifr.src = 'http://a.com/b.html';
ifr.style.display = 'none';
document.body.appendChild(ifr);
ifr.onload = function(){
  //獲取iframe的document物件
//W3C的標準方法是iframe.contentDocument, //IE6、7可以使用document.frames[ID].document //為了更好相容,可先獲取iframe的window物件iframe.contentWindow vardoc = ifr.contentDocument || ifr.contentWindow.document; // 在這裡操縱b.html alert(doc.getElementById("test").innerHTML);

而a.com/b.html:

<!DOCTYPE >
<html>
<head
>
<title></title> <scripttype="text/javascript"> document.domain='a.com';//提升域 </script> </head> <body> <h1id="test">Hello World</h1> </body> </html>

但是,這種方法只支援同一根域名下的頁面!baidu.com和google.com的話,想想就好(●’◡’●)

複習:本來在同一個 origin 下,父頁面可以通過 iframe.contentWindow 直接訪問 iframe 的全域性變數、DOM 樹等,iframe 可以也通過 parent/top 對父頁面做同樣的事情。

不同 origin 下,還有一種標準的方法是通過HTML5的 .postMessage() 互相通訊,不標準的方法是利用 location.hash 等奇技淫巧。

JSONP:

JSONP(JSON with Padding)是一個非官方的協議,它允許在伺服器端整合Script tags返回至客戶端,通過javascript callback的形式實現跨域訪問(這僅僅是JSONP簡單的實現形式)。

原理:含有src屬性的標籤都可以跨域,如img、script、iframe!

本質:只是執行了javascript指令碼!

簡單來說,就是在客戶端宣告回撥函式之後,客戶端通過script標籤向伺服器跨域請求資料,然後服務端返回相應的資料動態執行回撥函式(返回帶資料引數回撥函式的字串,在客戶端剛好被動態執行了!)。

看例項:

<script type="text/javascript">  
    function jsonpCallback(result) {  
        alert(result);  
    }  
    var JSONP=document.createElement("script");  
    JSONP.type="text/javascript";  
    JSONP.src="你的請求地址?callback=jsonpCallback";//在請求的地址後面帶上回調函式,客戶端宣告的jsonpCallback  
    document.getElementsByTagName("head")[0].appendChild(JSONP);  
</script>  

或者:


<script type="text/javascript">  
    //注意:這個回撥函式需要宣告在前,js連結呼叫在後!
    function jsonpCallback(result) {  
        alert(result);  
    }  
</script>  
<script type="text/javascript" src="你的請求地址?callback=jsonpCallback"></script>  

後臺返回的大概結果:

jsonpCallback({a:1,b:2});//類似的字串,在js指令碼中可以被動態執行,此來就可以拿到資料啦

但是,相應的弊端也出現了!這種方法每當頁面載入就執行,而不是按事件觸發去動態執行的!因而出現了jsonp的封裝,在需要的時候動態呼叫!

jsonp封裝:

大概形式:jsonp(url,data,callback)

大概步驟思路:

  1. 動態新增script標籤,並將其加入頁面中
  2. 組裝傳入的資料引數data
  3. 給每個回撥函式唯一命名,設定回撥請求成功或失敗之後的處理
  4. 最後無論請求成功還是失敗,都要刪除建立的javascript標籤。
function JSONP(url,config){  
    var data = config.data || [];  
    var paraArr=[],paraString='';//get請求的引數。  
    var urlArr;  
    var callbackName;//每個回撥函式一個名字。按時間戳。  
    var script,head;//要生成script標籤。head標籤。  
    var supportLoad;//是否支援 onload。是針對IE的相容處理。  
    var onEvent;//onload或onreadystatechange事件。  
    var timeout = config.timeout || 0;//超時功能。  

    for(var i in data){  
        if(data.hasOwnProperty(i)){  
            paraArr.push(encodeURIComponent(i) + "=" +encodeURIComponent(data[i]));  
        }  
    }  

    urlArr = url.split("?");//連結中原有的引數。  
    if(urlArr.length>1){  
        paraArr.push(urlArr[1]);  
    }  

    callbackName = 'callback'+new Date().getTime();  
    paraArr.push('callback='+callbackName);  
    paraString = paraArr.join("&");  
    url = urlArr[0] + "?"+ paraString;  

    script = document.createElement("script");  
    script.loaded = false;//為了實現IE下的onerror做的處理。JSONP的回撥函式總是在script的onload事件(IE為onreadystatechange)之前就被呼叫了。因此我們在正向回撥執行之時,為script標籤新增一個屬性,然後待到onload發生時,再檢測有沒有這個屬性就可以判定是否請求成功,沒有成功當然就呼叫我們的error。  

    //將回調函式新增到全域性。  
    window[callbackName] = function(arg){  
        var callback = config.callback;  
        callback(arg);  
        script.loaded = true;  
    }  

    head = document.getElementsByTagName("head")[0];  
    head.insertBefore(script, head.firstChild) //chrome下第二個引數不能為null  
    script.src = url;  

    supportLoad = "onload" in script;  
    onEvent = supportLoad ? "onload" : "onreadystatechange";  

    script[onEvent] = function(){  

        if(script.readyState && script.readyState !="loaded"){  
            return;  
        }  
        if(script.readyState == 'loaded' && script.loaded == false){  
            script.onerror();  
            return;  
        }  
        //刪除節點。  
        (script.parentNode && script.parentNode.removeChild(script))&& (head.removeNode && head.removeNode(this));    
        script = script[onEvent] = script.onerror = window[callbackName] = null;  

    }  

    script.onerror = function(){  
        if(window[callbackName] == null){  
            console.log("請求超時,請重試!");  
        }  
        config.error && config.error();//如果有專門的error方法的話,就呼叫。  
        (script.parentNode && script.parentNode.removeChild(script))&& (head.removeNode && head.removeNode(this));    
        script = script[onEvent] = script.onerror = window[callbackName] = null;  
    }  

    if(timeout!= 0){  
        setTimeout(function() {  
            if(script && script.loaded == false){  
                window[callbackName] = null;//超時,且未載入結束,登出函式  
                script.onerror();                 
            }  
        }, timeout);  
    }  

}  

jQuery中對JSONP的實現:

jQuery中提供了兩個方法來實現:$.getJSON()和$.ajax(),常用的是底層的$.ajax()方法!

1.$.getJSON():

$.getJSON(url?jsoncallback=?,data,fn(data){…})

關鍵點:在url後帶上引數jsoncallback=?,後臺返回隨機命名的callback函式!會被Jquery自動替換成回撥方法的名稱!

<script type="text/javascript">
 $.getJSON("http://localhost:3856/GetItemCates.ashx/GetItemCats?gateid=20&format=json&jsoncallback=?", function (data) { 
 var myprops = data.itemcats_get_response.item_cats.item_cat;
            $.each(myprops, function (index, item) { $("ul").append("<li>" + item.name + "," + item.cid + "</li>") });
        }

        );
</script>

2. $.ajax():

$.ajax({
url: url,
data: data,
dataType : “jsonp”,
jsonp: “jsoncallback”,
jsonpCallback:”success_jsonpCallback”,
success: callback
});

 $.ajax({
     type : "get", //jquey是不支援post方式跨域的
     async:false,
     url :"http://api.taobao.com/apitools/ajax_props.do", //跨域請求的URL
     dataType : "jsonp",//傳遞給請求處理程式,用以獲得jsonp回撥函式名的引數名(預設為:callback)
     jsonp: "jsoncallback", //自定義的jsonp回撥函式名稱,預設為jQuery自動生成的隨機函式名
     jsonpCallback:"success_jsonpCallback",//成功獲取跨域伺服器上的json資料後,會動態執行這個callback函式
     success : function(json){ 
             alert(json);
     }
});

以上的jsonp和jsonpCallback可以自定義回撥函式名和引數名!

注意:

  1. 需要伺服器端的支援才行。
  2. 只支援get請求

CORS跨域資源共享(真正跨域)

跨域資源共享(CORS )是一種網路瀏覽器的技術規範,它為Web伺服器定義了一種方式,允許網頁從不同的域訪問其資源。 CORS就是為了讓AJAX可以實現可控的跨域訪問而生的

CORS與JSONP相比:

  • JSONP只能實現GET請求,而CORS支援所有型別的HTTP請求
  • 使用CORS,開發者可以使用普通的XMLHttpRequest發起請求和獲得資料,比起JSONP有更好的錯誤處理。
  • JSONP主要被老的瀏覽器支援,但它們往往不支援CORS,而絕大多數現代瀏覽器都已經支援了CORS。

本質是HTML5 xhr level2原生ajax請求!

只需要在後臺中加上響應頭來允許域請求!在被請求的Response header中加入以下設定,就可以實現跨域訪問了!

//指定允許其他域名訪問
'Access-Control-Allow-Origin:*'//或指定域
//響應型別
'Access-Control-Allow-Methods:GET,POST'
//響應頭設定
'Access-Control-Allow-Headers:x-requested-with,content-type'

後臺的娃助我(●’◡’●)

總結:整理了同源策略限制,出現了跨域請求的需求,原生傳統的跨域請求方式,原生js和jQuery中對跨域的處理(JSONP),以及HTML5 中的CORS跨域資源共享。系統條理得梳理好傳說中的跨域了!o(^▽^)o