也谈跨域数据交互解决方案
先來句題外話,最開始Ajax應該是用來特指用XMLHttpRequest傳輸數據這門技術,但就像最近大家把一切web新技術都歸到html5名下一樣,現在一切異步獲取數據的手段都被人稱之為Ajax。
由于JavaScript同源策略的存在,跨域數據交互是個老生常談的話題了。網上相關文章很多,不過隨著時間的推移和瀏覽器的更新,一部分解決方案已經不適用了,同時也出現了一些更好的方法。拋開純服務器Proxy這種跟前端沒什么關系的方案不說,這里簡單總結下常見的其他幾種方式。
JSONP
JSONP是最常見的跨域數據交互的方式,原理是html的script標簽可以加載并執行其他域JS文件。站點B把要提供的數據作為參數傳給一個站點A定義的全局函數,站點A引用這個文件就可以跨域獲取數據了,A站還可以把少量參數放在script標簽的src里提交給B站。外鏈JS這種方案只支持GET,受IE下url長度不能超過2083個字節的限制和出于安全考慮,一般不用來提交數據。
有人通過后端Proxy使得這種方式可以獲取任意頁面內容,還增加了對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>實際上這個方案是借助后端把任意頁面輸出為JS變量,后端根據url中相關標識來決定請求方式和參數,并不能解決大數據提交問題。
原生表單+Redirect+Callback
原生的form表單支持提交數據到其他域,我們只需要把form的target指向頁面上的隱藏iframe,那就實現了無刷新提交,剩下的問題就是怎么獲取提交后的結果。例如站點A表單提交數據到站點B,通常我們會在站點B處理完請求,重定向到站點A下某個Proxy頁面,并在url帶上參數標識處理結果。接著,A站下的Proxy頁面就可以解析url參數,傳給父頁面的Callback函數來處理了。
Flash
利用flash的URLLoader,也可以輕松實現跨域數據交互。只要站點B的跨域策略文件(crossdomain.xml)中包含了站點A,A站就可以獲取B站的數據,提交數據給B站。我們可以把JS和flash的交互封裝一下,更方便的使用。這里有一個別人封裝好的版本,使用起來和原生的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
如果站點B有一個proxy頁面,用原生Ajax(XMLHttpRequest)對B站其他頁面進行各種數據交互,那么我們在A站用iFrame引入這個proxy頁面,只需要解決iFrame跨域問題就可以了。實際上,如果A和B屬于相同大域,設置兩邊的document.domain為根域名就OK了;如果是完全不同的兩個域,也有許多現成的解決方案,例如經典的window.name。更妙的是,除開IE6、IE7,幾乎所有現代瀏覽器都支持用window.postMessage實現不同iFrame的數據通訊。pmxdr就是這樣一個庫,利用postMessage把數據傳給隱藏的站外iFrame來實現跨域Ajax,libxdr對它進行了進一步的封裝,使之更好用:
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");值得注意的是,這種方案只需要在站點B部署一個proxy頁面,其他任意站點都可以通過這個頁面與之交互,不太安全,這一點pmxdr考慮到了,在pmxdr-host.js里有一個變量alwaysTrustedOrigins,它是一個數組,支持用正則定義允許交互的站點。
終極解決方案:CORS
實際上,除了IE6、IE7,大部分現代瀏覽器已經支持了跨域資源共享(Cross-Origin Resource Sharing,簡稱:CORS)標準,這可謂是跨域Ajax的終極解決方案。有了這個標準,只需要在Response Header里加上這么一條,就可以輕松跨域了:
Access-Control-Allow-Origin: http://hello-world.example這個header定義允許哪些域跟自己交互,如果定義為*就表示允許任何域,這么做當然是不推薦的。在除IE之外的標準瀏覽器,這樣就可以跨域Ajax了。對于IE,需要換用新增的XDomainRequest對象來發送請求,其它都類似。另外還有幾個header可以用來設置允許的提交方式等信息,如果要支持認證或者提交xml等格式的數據給服務器,則需要預請求,這里有更多說明。
總結
不同的方案有各自不同的使用場景,誰好誰壞不能一概而論。一般的,跨域獲取數據個人習慣用JSONP,跨域提交數據個人習慣用表單+Callback。隨著現代瀏覽器的普及,原生xhr也可以嘗試下,flash可以作為替補降級用。最后,如果要選用一個封裝了多種跨域實現的庫,可以考慮下Yui3的io組件。
本文提到一些組件鏈接:
- AJAX Cross Domain(在服務端把任意網頁轉為JS變量)
- Cross Domain XHR(基于flash,接口與xhr高度一致)
- pmxdr(使用postMessage和iframe的跨域方案)
- libxdr(pmxdr的再次封裝,接口模擬IE的XDomainRequest)
- Yui3的IO組件(基于flash或原生xhr的跨域方案)
轉載于:https://www.cnblogs.com/zhuyang/p/4793176.html
總結
以上是生活随笔為你收集整理的也谈跨域数据交互解决方案的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: html中,怎么样才能获得iframe页
- 下一篇: 防止NSTimer和调用对象之间的循环引