javascript中實現跨域的方式總結

js中的跨域請求應該也算是一個重點,具體什麼叫跨域,在這裏我就不展開了,能夠查一下瀏覽器的同源策略和跨域的定義。原來只知道經常使用的jsonp和document.domain這兩種方式,這幾天學習了一下其餘幾種跨域請求的方式,正好一塊兒作個總結。javascript

第一種方式:jsonp請求

jsonp請求應該是你們最爲熟悉的一種(至少是我知道的第一種跨域請求方式)。jsonp的原理是利用<script>標籤的跨域特性,能夠不受限制地從其餘域中加載資源,相似的標籤還有<img>.利用<script>標籤的這個特性能夠從服務器中返回須要的跨域數據。下面用代碼的小例子加以分析:
例子中html文件和後臺php(這裏我以php後臺爲例,後臺我本身會的是php)文件位於不一樣域中!php

先看html文件:
圖片描述
文件底部的<script>標籤的src連接到後臺service.php文件,並傳入參數callback,這裏的關鍵是把回調函數做爲參數傳給後臺。html

再看後臺php文件:
圖片描述html5

文件接收回調函數並把要返回的參數以參數注入的方式注入到回調函數中,再返回給客戶端。

這樣的話瀏覽器解析script標籤,並執行返回的js文檔,此時服務器返回的數據做爲參數,傳到頁面中定義好的 jsonpBack 函數裏.(動態執行回調函數),就拿到了咱們須要的跨域數據。java

這裏補充一點就是jquery對jsonp有着很好的支持,jquery中$.getJSON方法將jsonp的調用方式進行了封裝,用起來十分方便,使用的方式以下便可:jquery

<script>
$.getJSON("後臺文件地址?參數=**",function(jsondata){
//在這裏就能夠操做從後臺拿到的jsondata數據
})
</script>

第二種方式:document.domain

這種方式用在主域名相同子域名不一樣的跨域訪問中,舉個例子:http://a.frame.comhttp://b.frame.com 他們的主域名都是frame.com 這兩個域名中的文件能夠用這種方式進行訪問,經過在兩個域中具體的文件中設置document.domain="frame.com"就可達到跨域訪問的目的。web

實際應用中經常用在iframe中窗口之間的訪問,根據瀏覽器的同源策略,瀏覽器中不一樣域的框架之間是不能進行js的交互操做的,因此一個窗口是不能拿到另外一個窗口中的contentWindow對象的屬性和方法的(注意是能拿到contentWindow對象的,只是屬性和方法都不可用)。爲了能拿到數據,只要在兩個iframe中分別寫入document.domain="主域名",這樣設置以後,就能拿到contentWindow對象的屬性和方法了。代碼就這麼簡單的一行,我就不寫小例子了。json

第三種方式:window.name

window的name屬性有個特徵:在一個窗口(window)的生命週期內,窗口載入的全部的頁面都是共享一個window.name的,每一個頁面對window.name都有讀寫的權限,window.name是持久存在一個窗口載入過的全部頁面中的,並不會因新頁面的載入而進行重置。跨域

這是什麼意思呢?通俗來說,就是好比我在a.html這個頁面中設置了window.name="a";而後讓window從新加載b.html頁面,而後在b.html頁面中輸出window.name會發現window.name=「a」。因此就算a.html和b.html這兩個頁面不是在同一個域中的,也能夠在b頁面中拿到a頁面設置的window.name的值,跨域的核心思路就是這個原理。瀏覽器

實際應用中也是經常用在兩個iframe之間(須要結合iframe的特性來用),先上一張從別人那邊借鑑過來的原理圖,我再按照本身的理解進行分析:
圖片描述

圖中有三個頁面,getDomainData.html是獲取數據的頁面,null.html是一個和getDomainData.html同域的空頁面,它的做用是做爲一箇中轉站,進行數據的過渡。data.html是要獲取數據的所在頁面,這裏有着咱們要的數據,它和getDomainData.html處於不一樣域中,因此取數據是跨域訪問。具體的過程是這樣的:在getDomainData.html中創建一個子頁面iframe,把這個iframe的src指向b.com/data.html,這樣當這個iframe加載完成後就能夠訪問到data.html中的window.name的數據,以後再將iframe的src改成a.com/null.html,跳回getDomainData.html的同一個域,這樣根據同源策略,getDomainData.html就能夠訪問到null.html中取得的data.html的數據了。獲取數據之後最好銷燬這個iframe,釋放掉內存,也保證了安全。下面附上代碼小例子:

getDomainData.html:

<script type="text/javascript">
     var flag=0;
     var data;
     var iframe=document.createElement("iframe");//建立一箇中轉站iframe
     document.appendChild(iframe);
     getData=function(){//iframe加載完成後調用的處理函數
         if(flag==1){
             data=iframe.contentWindow.name;//讀取b.html中的window.name
         }else{
             flag=1;
             iframe.src="http://a.com/null.html";//跳回getDomainData.html的同一個域
         }
    };
     iframe.src="http://b.com/data.html";//設置src到要得到數據的域中的對應頁面

    if (iframe.attachEvent) {//兼容IE,監聽iframe加載完成
        iframe.attachEvent('onload', getData);
    } else {
        iframe.addEventListener('load',getData);
    }

</script>

data.html:

<script>
window.name="被獲取數據"//簡單的一行代碼
</script>

最後的iframe銷燬:

<script>
    iframe.contentWindow.document.write("");//狀況iframe中的內容
    iframe.contentWindow.close();//避免iframe的內存泄漏
    document.body.removeChild(iframe);//移除iframe節點
</script>

第四種方式:window.postMessage

window.postMessages是html5中實現跨域訪問的一種新方式,可使用它來向其它的window對象發送消息,不管這個window對象是屬於同源或不一樣源。
該方式的使用仍是十分簡單的,給要發送數據的頁面中的window對象調用一個postMessage(message,targetOrigin)方法便可,該方法的第一個參數message爲要發送的消息,類型只能爲字符串;第二個參數targetOrigin用來限定接收消息的那個window對象所在的域,若是不想限定域,直接使用通配符 * 。再讓接收數據頁面的window對象監聽自身的message事件來獲取傳過來的消息,消息內容儲存在該事件對象的data屬性中。簡單的小例子以下:

test.html(發送頁面):

<script>
<iframe name="receive" id="iframe" src="test2.html" scrolling="no">

</iframe>
<script type="text/javascript">

window.onload=function(){
    var iframWindow = document.getElementById("iframe").contentWindow;
    iframWindow.postMessage("A secret", "*");//發送消息
    
}
</script>

test2.html(接收頁面)

<script>
    window.addEventListener("message",function(e){
        alert(e.data);//接收到的消息
    })
</script>

測試結果以下
圖片描述

關於這個用法困擾了我很久,困擾1:由於最後彈出的消息是在test.html,而不是在test2.html中,我不肯定是由於test.html包含了test2.html,因此瀏覽器渲染才彈出的alert,仍是test2.html接收到消息的反饋。後來查閱了權威的文檔,纔有了進一步的理解,應該是test2.html收到消息,以iframe在test.html中渲染加載出的。
困擾2:postMessage的調用對象是目標窗口仍是發送窗口,是否能以window形式調用?
查閱文檔後得出結論:*postMessage的調用對象,是其餘窗口的一個引用,即目標窗口,不是要發送的窗口,(這裏比較出乎意料) 並且postMessage想要通訊必須使得一個窗口以iframe的形式存在於另外一個窗口,或者一個窗口是從另外一個窗口經過window.open()或者超連接的形式打開的(一樣能夠用window.opener獲取源窗口);換句話說,你要交換數據,必須能獲取目標窗口(target window)的引用,否則兩個窗口之間毫無聯繫,想通訊也無能爲力,因此不能直接以主頁面window的形式調用。

具體的權威解釋請看這個連接: window.postMessage

第五種方式:CORS

CORS(Corss-Origin-Resource Sharing,跨源資源共享),是一種網絡瀏覽器的技術規範,它爲Web服務器定義了一種方式,容許網頁從不一樣的域訪問其資源,而這種訪問是被同源策略所禁止的。CORS系統定義了一種瀏覽器和服務器交互的方式來肯定是否容許跨域請求。 它是一個妥協,有更大的靈活性,但比起簡單地容許全部這些的要求來講更加安全。
CORS背後的基本思想,就是使用自定義的HTTP頭部讓瀏覽器與服務器進行溝通,從而決定請求或響應是應該成功仍是應該失敗。

CORS的使用仍是十分簡便的,好比一個簡單的GET或者POST請求,在發送的時候給它附加一個額外的origin頭部,其中包含請求頁面的源信息(協議、域名和端口),以便服務器根據這個頭部信息來決定是否給以響應。下面是javascript高級程序設計書上的一個小例子:

Origin:http://www.nczonline.net

若是服務器認爲這個請求能夠接受,就在Access-Control-Allow-Origin頭部中回發相同的源消息(若是是公共資源,能夠回發「 * 」)。例如:

Access-Control-Allow-Origin:http://www.nczonline.net

這樣設置以後,服務器與瀏覽器就能夠進行跨域信息的交換了。具體的在不一樣瀏覽器上的支持和使用,我就不展開了,js高級程序設計書上提到了不少,網上查一下也有不少。

第六種方式:Web Sockets

Web Sockets是一種新瀏覽器API,能在一個單獨的持久鏈接上提供全雙工、雙向通訊,使用ws(代替http://)或wss(代替https://)協議,可用於任意的客戶端和服務器程序。
web sockets原理:在JS建立了web socket以後,會有一個HTTP請求發送到瀏覽器以發起鏈接。取得服務器響應後,創建的鏈接會使用HTTP升級從HTTP協議交換爲web sockt協議。
使用的小例子:

// 建立一個Socket實例
var socket = new WebSocket('ws://www.example.com/server.php'); 

// 打開Socket 
socket.onopen = function(event) { 

  // 發送消息
  socket.send('a secret'); 

  // 監聽消息的接收
  socket.onmessage = function(event) { 
    var data=event.data;
    //處理data...
  }; 

  // 監聽socket的關閉
  socket.onclose = function(event) { 
    console.log('socket has closed',event); 
  }; 

  // 關閉Socket
  //socket.close() 
};

一樣附上權威文檔供參考:Web Workers API

總結

除此以外還有一些跨域訪問的方式:好比Comet、圖像Ping、SSE等,感興趣的能夠直接查找這些內容。在這些跨域訪問方式上,各有各的適用訪問和相應的限制,須要結合實際來適用。我在這裏有個疑問:我本身運用的比較多的就是jsonp這種方式,那麼在實際開發中,比較推崇的跨域訪問方式是哪一種呢?還有就是html5的postMessage是否是能夠取代window.name(一樣都須要一個iframe做爲中間媒介)這類訪問方式,這個新API方式是否是在實際中頗有效呢?但願有經驗的大牛能夠解答,不勝感激。

相關文章
相關標籤/搜索