javascript
javascript中实现跨域的方式总结
js中的跨域請求應(yīng)該也算是一個重點,具體什么叫跨域,在這里我就不展開了,可以查一下瀏覽器的同源策略和跨域的定義。原來只知道常用的jsonp和document.domain這兩種方式,這幾天學(xué)習(xí)了一下其他幾種跨域請求的方式,正好一起做個總結(jié)。
第一種方式:jsonp請求
jsonp請求應(yīng)該是大家最為熟悉的一種(至少是我知道的第一種跨域請求方式)。jsonp的原理是利用<script>標(biāo)簽的跨域特性,可以不受限制地從其他域中加載資源,類似的標(biāo)簽還有<img>.利用<script>標(biāo)簽的這個特性可以從服務(wù)器中返回需要的跨域數(shù)據(jù)。下面用代碼的小例子加以分析:
例子中html文件和后臺php(這里我以php后臺為例,后臺我自己會的是php)文件位于不同域中!
先看html文件:
文件底部的<script>標(biāo)簽的src鏈接到后臺service.php文件,并傳入?yún)?shù)callback,這里的關(guān)鍵是把回調(diào)函數(shù)作為參數(shù)傳給后臺。
再看后臺php文件:
這樣的話瀏覽器解析script標(biāo)簽,并執(zhí)行返回的js文檔,此時服務(wù)器返回的數(shù)據(jù)作為參數(shù),傳到頁面中定義好的 jsonpBack 函數(shù)里.(動態(tài)執(zhí)行回調(diào)函數(shù)),就拿到了我們需要的跨域數(shù)據(jù)。
這里補充一點就是jquery對jsonp有著很好的支持,jquery中$.getJSON方法將jsonp的調(diào)用方式進行了封裝,用起來十分方便,使用的方式如下即可:
<script> $.getJSON("后臺文件地址?參數(shù)=**",function(jsondata){ //在這里就可以操作從后臺拿到的jsondata數(shù)據(jù) }) </script>第二種方式:document.domain
這種方式用在主域名相同子域名不同的跨域訪問中,舉個例子:http://a.frame.com和http://b.frame.com 他們的主域名都是frame.com 這兩個域名中的文件可以用這種方式進行訪問,通過在兩個域中具體的文件中設(shè)置document.domain="frame.com"就可達到跨域訪問的目的。
實際應(yīng)用中常常用在iframe中窗口之間的訪問,根據(jù)瀏覽器的同源策略,瀏覽器中不同域的框架之間是不能進行js的交互操作的,所以一個窗口是不能拿到另一個窗口中的contentWindow對象的屬性和方法的(注意是能拿到contentWindow對象的,只是屬性和方法都不可用)。為了能拿到數(shù)據(jù),只要在兩個iframe中分別寫入document.domain="主域名",這樣設(shè)置之后,就能拿到contentWindow對象的屬性和方法了。代碼就這么簡單的一行,我就不寫小例子了。
第三種方式:window.name
window的name屬性有個特征:在一個窗口(window)的生命周期內(nèi),窗口載入的所有的頁面都是共享一個window.name的,每個頁面對window.name都有讀寫的權(quán)限,window.name是持久存在一個窗口載入過的所有頁面中的,并不會因新頁面的載入而進行重置。
這是什么意思呢?通俗來講,就是比如我在a.html這個頁面中設(shè)置了window.name="a";然后讓window重新加載b.html頁面,然后在b.html頁面中輸出window.name會發(fā)現(xiàn)window.name=“a”。所以就算a.html和b.html這兩個頁面不是在同一個域中的,也可以在b頁面中拿到a頁面設(shè)置的window.name的值,跨域的核心思路就是這個原理。
實際應(yīng)用中也是常常用在兩個iframe之間(需要結(jié)合iframe的特性來用),先上一張從別人那邊借鑒過來的原理圖,我再按照自己的理解進行分析:
圖中有三個頁面,getDomainData.html是獲取數(shù)據(jù)的頁面,null.html是一個和getDomainData.html同域的空頁面,它的作用是作為一個中轉(zhuǎn)站,進行數(shù)據(jù)的過渡。data.html是要獲取數(shù)據(jù)的所在頁面,這里有著我們要的數(shù)據(jù),它和getDomainData.html處于不同域中,所以取數(shù)據(jù)是跨域訪問。具體的過程是這樣的:在getDomainData.html中建立一個子頁面iframe,把這個iframe的src指向b.com/data.html,這樣當(dāng)這個iframe加載完成后就可以訪問到data.html中的window.name的數(shù)據(jù),之后再將iframe的src改為a.com/null.html,跳回getDomainData.html的同一個域,這樣根據(jù)同源策略,getDomainData.html就可以訪問到null.html中取得的data.html的數(shù)據(jù)了。獲取數(shù)據(jù)以后最好銷毀這個iframe,釋放掉內(nèi)存,也保證了安全。下面附上代碼小例子:
getDomainData.html:
<script type="text/javascript">var flag=0;var data;var iframe=document.createElement("iframe");//創(chuàng)建一個中轉(zhuǎn)站iframedocument.appendChild(iframe);getData=function(){//iframe加載完成后調(diào)用的處理函數(shù)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";//設(shè)置src到要獲得數(shù)據(jù)的域中的對應(yīng)頁面if (iframe.attachEvent) {//兼容IE,監(jiān)聽iframe加載完成iframe.attachEvent('onload', getData);} else {iframe.addEventListener('load',getData);}</script>data.html:
<script> window.name="被獲取數(shù)據(jù)"//簡單的一行代碼 </script>最后的iframe銷毀:
<script>iframe.contentWindow.document.write("");//情況iframe中的內(nèi)容iframe.contentWindow.close();//避免iframe的內(nèi)存泄漏document.body.removeChild(iframe);//移除iframe節(jié)點 </script>第四種方式:window.postMessage
window.postMessages是html5中實現(xiàn)跨域訪問的一種新方式,可以使用它來向其它的window對象發(fā)送消息,無論這個window對象是屬于同源或不同源。
該方式的使用還是十分簡單的,給要發(fā)送數(shù)據(jù)的頁面中的window對象調(diào)用一個postMessage(message,targetOrigin)方法即可,該方法的第一個參數(shù)message為要發(fā)送的消息,類型只能為字符串;第二個參數(shù)targetOrigin用來限定接收消息的那個window對象所在的域,如果不想限定域,直接使用通配符 * 。再讓接收數(shù)據(jù)頁面的window對象監(jiān)聽自身的message事件來獲取傳過來的消息,消息內(nèi)容儲存在該事件對象的data屬性中。簡單的小例子如下:
test.html(發(fā)送頁面):
<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", "*");//發(fā)送消息} </script>test2.html(接收頁面)
<script>window.addEventListener("message",function(e){alert(e.data);//接收到的消息}) </script>測試結(jié)果如下
關(guān)于這個用法困擾了我好久,困擾1:因為最后彈出的消息是在test.html,而不是在test2.html中,我不確定是因為test.html包含了test2.html,所以瀏覽器渲染才彈出的alert,還是test2.html接收到消息的反饋。后來查閱了權(quán)威的文檔,才有了進一步的理解,應(yīng)該是test2.html收到消息,以iframe在test.html中渲染加載出的。
困擾2:postMessage的調(diào)用對象是目標(biāo)窗口還是發(fā)送窗口,是否能以window形式調(diào)用?
查閱文檔后得出結(jié)論:*postMessage的調(diào)用對象,是其他窗口的一個引用,即目標(biāo)窗口,不是要發(fā)送的窗口,(這里比較出乎意料) 而且postMessage想要通信必須使得一個窗口以iframe的形式存在于另一個窗口,或者一個窗口是從另一個窗口通過window.open()或者超鏈接的形式打開的(同樣可以用window.opener獲取源窗口);換句話說,你要交換數(shù)據(jù),必須能獲取目標(biāo)窗口(target window)的引用,不然兩個窗口之間毫無聯(lián)系,想通信也無能為力,所以不能直接以主頁面window的形式調(diào)用。
具體的權(quán)威解釋請看這個鏈接: window.postMessage
第五種方式:CORS
CORS(Corss-Origin-Resource Sharing,跨源資源共享),是一種網(wǎng)絡(luò)瀏覽器的技術(shù)規(guī)范,它為Web服務(wù)器定義了一種方式,允許網(wǎng)頁從不同的域訪問其資源,而這種訪問是被同源策略所禁止的。CORS系統(tǒng)定義了一種瀏覽器和服務(wù)器交互的方式來確定是否允許跨域請求。 它是一個妥協(xié),有更大的靈活性,但比起簡單地允許所有這些的要求來說更加安全。
CORS背后的基本思想,就是使用自定義的HTTP頭部讓瀏覽器與服務(wù)器進行溝通,從而決定請求或響應(yīng)是應(yīng)該成功還是應(yīng)該失敗。
CORS的使用還是十分簡便的,比如一個簡單的GET或者POST請求,在發(fā)送的時候給它附加一個額外的origin頭部,其中包含請求頁面的源信息(協(xié)議、域名和端口),以便服務(wù)器根據(jù)這個頭部信息來決定是否給以響應(yīng)。下面是javascript高級程序設(shè)計書上的一個小例子:
Origin:http://www.nczonline.net如果服務(wù)器認為這個請求可以接受,就在Access-Control-Allow-Origin頭部中回發(fā)相同的源消息(如果是公共資源,可以回發(fā)“ * ”)。例如:
Access-Control-Allow-Origin:http://www.nczonline.net這樣設(shè)置之后,服務(wù)器與瀏覽器就可以進行跨域信息的交換了。具體的在不同瀏覽器上的支持和使用,我就不展開了,js高級程序設(shè)計書上提到了很多,網(wǎng)上查一下也有很多。
第六種方式:Web Sockets
Web Sockets是一種新瀏覽器API,能在一個單獨的持久連接上提供全雙工、雙向通信,使用ws(代替http://)或wss(代替https://)協(xié)議,可用于任意的客戶端和服務(wù)器程序。
web sockets原理:在JS創(chuàng)建了web socket之后,會有一個HTTP請求發(fā)送到瀏覽器以發(fā)起連接。取得服務(wù)器響應(yīng)后,建立的連接會使用HTTP升級從HTTP協(xié)議交換為web sockt協(xié)議。
使用的小例子:
同樣附上權(quán)威文檔供參考:Web Workers API
總結(jié)
除此之外還有一些跨域訪問的方式:比如Comet、圖像Ping、SSE等,感興趣的可以直接查找這些內(nèi)容。在這些跨域訪問方式上,各有各的適用訪問和相應(yīng)的限制,需要結(jié)合實際來適用。我在這里有個疑問:我自己運用的比較多的就是jsonp這種方式,那么在實際開發(fā)中,比較推崇的跨域訪問方式是哪種呢?還有就是html5的postMessage是不是可以取代window.name(同樣都需要一個iframe作為中間媒介)這類訪問方式,這個新API方式是不是在實際中很有效呢?希望有經(jīng)驗的大牛可以解答,不勝感激。
《新程序員》:云原生和全面數(shù)字化實踐50位技術(shù)專家共同創(chuàng)作,文字、視頻、音頻交互閱讀總結(jié)
以上是生活随笔為你收集整理的javascript中实现跨域的方式总结的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 在centos6.5上安装mongodb
- 下一篇: 当页面有多个js文件时,应如何引入?