跨域方法:JSONP、iframe


同源策略:瀏覽器出於安全考慮,會限制文檔或腳本中發起的跨域請求(但src請求不受此限)資源的加載。實際上通過抓包軟件可以發現請求和響應都會成功,但是響應數據並不會被瀏覽器加載。不同源的客戶端腳本(javascript、ActionScript)在沒明確授權的情況下,不可以使用 XMLHttpRequest 對象和Fetch發起讀寫其他web服務器的資源,主要有如下限制:

1.) Cookie、LocalStorage 和 IndexDB 無法讀取 2.) DOM 和 Js對象無法讀寫 3.) AJAX 請求不能發送

同源不是指腳本的來源,而是指腳本所嵌入的文檔來源。具體要求域名,協議和端口都相同。

比如需要加載另一個域的一張圖片資源,通過<img src='xxx'>靜態引用的方式可以成功;但是通過ajax方法會報錯:No 'Access-Control-Allow-Origin' header is present on the requested resource.

如果只是希望用post提交數據,不需要處理響應數據,可以:

  a.用form的submit(跨域)提交最簡單,此時會導致當前頁面跳轉到別的域。可以將form的target設置到一個隱藏的iframe解決。

  b.用ajax的post方法,但是不能監聽到響應,而且會報錯。

跨域請求數據的方法:

JSONP:最簡單,支持度高,但只能發送get請求,傳遞的參數大小有限。用js動態創建一個script標簽,其中src=’www.targetweb.com?callback=foo’,然后定義foo(data)方法,最后將script標簽append到頁面。sript標簽會自動向目標網站發送請求,服務器將數據作為參數同函數名一起返回(foo(jsonData)函數調用表達式)。實際上是返回了<script>foo(xxxx)</script>腳本文件,所以函數foo會立即調用。

//JSONP異常處理
var ele = document.createElement('script');
ele.type = "text/javascript";
ele.src = 'www.targetweb.com?callback=foo';
//異常時的回調
ele.onerror = function() {
    console.log('error');
};
//js成功加載(下載並執行完之后)后的回調
ele.onload = function() {
    console.log('loaded');
};
document.body.appendChild(ele);

相當於添加了如下script標簽:

<script>
    function foo(jsonpData){
    //...
    }
</script>
<script src=’www.targetweb.com?callback=foo&param1=paramSubmitByUrl’></script>
<!--后台返回的js文件實際上就是函數調用:foo(jsonpData) -->

jQuery中的跨域請求

$.ajax({
    type : "get",
    async:true,
    url : "/taotao-rest/category",    //跨域的請求地址
    crossDomain:true,
    dataType : "jsonp",
    jsonp: "callbackName",//(不必要)定義請求參數名(相當於表單的name),供后台獲取參數使用,默認為"&callback=?"中的callback
    jsonpCallback:"showLists",//(不必要)自定義jsonp回調函數名稱,默認為jQuery自動生成的隨機函數名替代上述?號;在本例中后台應該返回showLists(jsonData)
    success : function(json){
        //為什么不定義jsonpCallback也不會報錯?因為jQuery內部會自定義jsonpCallback(若無),並用responseContainer接受后台返回的數據再存入json變量中。
        //window[ callbackName ] = function() {
        //    responseContainer = arguments;
        //};
        //最后jQuery之后會將callBackName和對應的script標簽刪除
        //所以此時才可以直接用json數據
    },  
    beforeSend: function(){
    //jsonp方式時此方法不會觸發,因為不是用 XHR發送的請求
    },
    error: function(xhr){
    //jsonp 方式時此方法不會觸發
    }
});

H5的WebSocket:使用ws和wss作為協議,允許跨域通信,可以相互推送信息。

iframe方式
因為jsonp只能發送get請求,所以對於跨域post提交大量數據時無能為力,此時可以用iframe跨域提交post請求。(兼容性好,兼容ie7/8/9)

<iframe>元素表示嵌套的瀏覽上下文,可以有效地將另一個HTML頁面嵌入到當前頁面中。實踐中常將form表單的target設置為iframe的name屬性,這樣submit提交后服務器返回的頁面就會在iframe中顯示,主頁面不會再刷新。iframe常用屬性:

name屬性
嵌入的iframe的名稱,該名稱可以用作<a>標簽的target屬性值,在指定的框架中打開被鏈接文檔;也可用作<form>標簽的target屬性值,規定在何處打開action URL。或<input>/<button>標簽的formtaget屬性值,規定表示提交表單后在哪里顯示接收到響應的名稱或關鍵詞,formtarget 屬性會覆蓋 <form> 元素的 target 屬性。

src
規定在 iframe 中顯示的文檔的 URL。

與iframe的交互:
  在主頁面獲取iframe內的文檔對象:document.getElementById('childiframe').contentDocument.xxx
  在iframe中的腳本訪問主頁面的window的屬性:window.parent.document
  在主頁面訪問iframe中window里的屬性:document.getElementById('childiframe').contentWindow
注意:如果iframe和主頁面不在同一個域,則不能通過js互相訪問(window或DOM),被瀏覽器限制了。把兩個子頁面的document.domain都指向主域則可以互相訪問。 

iframe的弊端
window 的 onload 事件需要在iframe 的所有資源加載完畢才會觸發,另外會占用主頁面的可用連接數量。所以一般盡量少用iframe,要用的話最好等主頁面加載完后再設置iframe的src加載內容。

使用iframe跨域

主要是利用Window.name屬性傳遞數據。每個iframe都有window對象,其name屬性用於標記window的名字,可以存貯2M大小的數據。除非主動修改或關閉,同一個window打開任意頁面其值都不變。window.name和iframe.name是2個概念,初始化時window.name會默認取當前iframe的name的值,但是之后就無關聯。修改window.name不影響iframe.name。只有在DOM對象下可以修改iframe的name屬性。另外在設置a元素和form表單的target屬性時,window.name優先於iframe.name。

步驟:

1.使用post提交數據

  使用主頁面的form跨域提交post數據,target指定到隱藏的iframe;或者在隱藏的iframe中用ajax提交post數據;

2.服務器端返回跨域下的任意空文檔,script部分需要設置window.name=data,data為服務器響應的數據。

3.將iframe的src設置為主頁面的域,否則無法訪問iframe的window。之后再從iframe的contentWindow.name中取數據。

//主頁面
var isloaded=false;    //防止循環刷新iframe
//chrome和ie下onload事件的觸發時機不一樣?!!!
document.getElementById('iframe0').onload=function (e) {
    if(!isloaded){
        isloaded=true;
        e.target.src='null.html';//設主頁面同域的任意頁面,響應數據在腳本中
    }else{
      //使用跨域獲取的數據
      console.log(document.getElementById('iframe0').contentWindow.name);
    }
}

CORS(Cross-origin resource sharing)跨域資源共享
如果只需要兼容IE11,則可以使用CORS。服務器實現CORS接口(在header配置Access-Control-Allow-origin屬性),瀏覽器不會丟棄響應,就可以跨源通信了。

var xhr = new XMLHttpRequest();
xhr.open('GET', 'http://example.com/', true);
xhr.withCredentials = true;

websocket協議跨域

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM