也谈跨域数据交互解决方案
先來句題外話,最開始Ajax應(yīng)該是用來特指用XMLHttpRequest傳輸數(shù)據(jù)這門技術(shù),但就像最近大家把一切web新技術(shù)都?xì)w到html5名下一樣,現(xiàn)在一切異步獲取數(shù)據(jù)的手段都被人稱之為Ajax。
由于JavaScript同源策略的存在,跨域數(shù)據(jù)交互是個(gè)老生常談的話題了。網(wǎng)上相關(guān)文章很多,不過隨著時(shí)間的推移和瀏覽器的更新,一部分解決方案已經(jīng)不適用了,同時(shí)也出現(xiàn)了一些更好的方法。拋開純服務(wù)器Proxy這種跟前端沒什么關(guān)系的方案不說,這里簡單總結(jié)下常見的其他幾種方式。
JSONP
JSONP是最常見的跨域數(shù)據(jù)交互的方式,原理是html的script標(biāo)簽可以加載并執(zhí)行其他域JS文件。站點(diǎn)B把要提供的數(shù)據(jù)作為參數(shù)傳給一個(gè)站點(diǎn)A定義的全局函數(shù),站點(diǎn)A引用這個(gè)文件就可以跨域獲取數(shù)據(jù)了,A站還可以把少量參數(shù)放在script標(biāo)簽的src里提交給B站。外鏈JS這種方案只支持GET,受IE下url長度不能超過2083個(gè)字節(jié)的限制和出于安全考慮,一般不用來提交數(shù)據(jù)。
有人通過后端Proxy使得這種方式可以獲取任意頁面內(nèi)容,還增加了對POST的支持,如:
HTML<script type="text/javascript" src="http://www.ajax-cross-domain.com/cgi-bin/ACD/ACD.js?uri=(http://www.google.com)"></script> <script type="text/javascript"> alert(ACD.responseText); </script> <script type="text/javascript" src="http://www.ajax-cross-domain.com/cgi-bin/ACD/ACD.js?uri=(http://216.92.131.147/dotserv/ACD/runit/post.cgi)&method=post&postdata=(name=fred&email=fred@fred.com)"></script> <script type="text/javascript"> alert(ACD.responseText); </script>實(shí)際上這個(gè)方案是借助后端把任意頁面輸出為JS變量,后端根據(jù)url中相關(guān)標(biāo)識來決定請求方式和參數(shù),并不能解決大數(shù)據(jù)提交問題。
原生表單+Redirect+Callback
原生的form表單支持提交數(shù)據(jù)到其他域,我們只需要把form的target指向頁面上的隱藏iframe,那就實(shí)現(xiàn)了無刷新提交,剩下的問題就是怎么獲取提交后的結(jié)果。例如站點(diǎn)A表單提交數(shù)據(jù)到站點(diǎn)B,通常我們會(huì)在站點(diǎn)B處理完請求,重定向到站點(diǎn)A下某個(gè)Proxy頁面,并在url帶上參數(shù)標(biāo)識處理結(jié)果。接著,A站下的Proxy頁面就可以解析url參數(shù),傳給父頁面的Callback函數(shù)來處理了。
Flash
利用flash的URLLoader,也可以輕松實(shí)現(xiàn)跨域數(shù)據(jù)交互。只要站點(diǎn)B的跨域策略文件(crossdomain.xml)中包含了站點(diǎn)A,A站就可以獲取B站的數(shù)據(jù),提交數(shù)據(jù)給B站。我們可以把JS和flash的交互封裝一下,更方便的使用。這里有一個(gè)別人封裝好的版本,使用起來和原生的XMLHttpRequest幾乎一模一樣:
JSvar req; function callback() { if (req.readyState == 4) { try { if (req.status != 200) { alert('error detected 1'); } else { alert("got data: "+req.responseText); } } catch(e) { alert('error detected 2'); } } } function test_get() { req = new CrossXHR(); req.onreadystatechange = callback; req.open('GET', 'http://www.pliantdev.com/support/test.xml'); req.send(); }Iframe+XMLHttpRequest
如果站點(diǎn)B有一個(gè)proxy頁面,用原生Ajax(XMLHttpRequest)對B站其他頁面進(jìn)行各種數(shù)據(jù)交互,那么我們在A站用iFrame引入這個(gè)proxy頁面,只需要解決iFrame跨域問題就可以了。實(shí)際上,如果A和B屬于相同大域,設(shè)置兩邊的document.domain為根域名就OK了;如果是完全不同的兩個(gè)域,也有許多現(xiàn)成的解決方案,例如經(jīng)典的window.name。更妙的是,除開IE6、IE7,幾乎所有現(xiàn)代瀏覽器都支持用window.postMessage實(shí)現(xiàn)不同iFrame的數(shù)據(jù)通訊。pmxdr就是這樣一個(gè)庫,利用postMessage把數(shù)據(jù)傳給隱藏的站外iFrame來實(shí)現(xiàn)跨域Ajax,libxdr對它進(jìn)行了進(jìn)一步的封裝,使之更好用:
JSvar req = new XDR(); req.open("POST", "http://code.eligrey.com/pmxdr/libxdr/demo.php"); req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); req.onload = function() { alert(this.responseText); // alerts "foo is bar" }; request.send("foo=bar");值得注意的是,這種方案只需要在站點(diǎn)B部署一個(gè)proxy頁面,其他任意站點(diǎn)都可以通過這個(gè)頁面與之交互,不太安全,這一點(diǎn)pmxdr考慮到了,在pmxdr-host.js里有一個(gè)變量alwaysTrustedOrigins,它是一個(gè)數(shù)組,支持用正則定義允許交互的站點(diǎn)。
終極解決方案:CORS
實(shí)際上,除了IE6、IE7,大部分現(xiàn)代瀏覽器已經(jīng)支持了跨域資源共享(Cross-Origin Resource Sharing,簡稱:CORS)標(biāo)準(zhǔn),這可謂是跨域Ajax的終極解決方案。有了這個(gè)標(biāo)準(zhǔn),只需要在Response Header里加上這么一條,就可以輕松跨域了:
Access-Control-Allow-Origin: http://hello-world.example這個(gè)header定義允許哪些域跟自己交互,如果定義為*就表示允許任何域,這么做當(dāng)然是不推薦的。在除IE之外的標(biāo)準(zhǔn)瀏覽器,這樣就可以跨域Ajax了。對于IE,需要換用新增的XDomainRequest對象來發(fā)送請求,其它都類似。另外還有幾個(gè)header可以用來設(shè)置允許的提交方式等信息,如果要支持認(rèn)證或者提交xml等格式的數(shù)據(jù)給服務(wù)器,則需要預(yù)請求,這里有更多說明。
總結(jié)
不同的方案有各自不同的使用場景,誰好誰壞不能一概而論。一般的,跨域獲取數(shù)據(jù)個(gè)人習(xí)慣用JSONP,跨域提交數(shù)據(jù)個(gè)人習(xí)慣用表單+Callback。隨著現(xiàn)代瀏覽器的普及,原生xhr也可以嘗試下,flash可以作為替補(bǔ)降級用。最后,如果要選用一個(gè)封裝了多種跨域?qū)崿F(xiàn)的庫,可以考慮下Yui3的io組件。
本文提到一些組件鏈接:
- AJAX Cross Domain(在服務(wù)端把任意網(wǎng)頁轉(zhuǎn)為JS變量)
- Cross Domain XHR(基于flash,接口與xhr高度一致)
- pmxdr(使用postMessage和iframe的跨域方案)
- libxdr(pmxdr的再次封裝,接口模擬IE的XDomainRequest)
- Yui3的IO組件(基于flash或原生xhr的跨域方案)
轉(zhuǎn)載于:https://www.cnblogs.com/zhuyang/p/4793176.html
總結(jié)
以上是生活随笔為你收集整理的也谈跨域数据交互解决方案的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: html中,怎么样才能获得iframe页
- 下一篇: 防止NSTimer和调用对象之间的循环引