Javascript跨域訪問解決方案
原文地址:http://blog.csdn.net/lovingprince/article/details/2954675
由於安全方面的考慮, Javascript 被限制了跨域訪問的能力,但是有時候我們希望能夠做一些合理的跨域訪問的事情,那麼怎麼辦呢?
這裡分兩類情況:
一、基於同一父域的子域之間頁面的訪問
參見如下 3 個 domain 域:
1 、 taobao.com
2 、 jipiao.taobao.com
3 、 promotion.taobao.com
它們有相同的父域 taobao.com
二、基於不同父域頁面之間的訪問
參見如下 3 個 domain 域:
1 、 taobao.com
2 、 baidu.com
3 、 sina.com.cn
它們具有不同的父域。
解決它們之間跨域的方案:
<!--[if !supportLists]-->①<!--[endif]-->伺服器 Proxy: 域 A 的頁面 JS 需要訪問域 B 下的連結獲取資料,該方案在域 A 的伺服器端建立一個Proxy 程式 ( 可能是 ASP 、 servlet 等任何服務端程式 ) ,域 A 的頁面 JS 直接呼叫本域下的 Proxy 程式, proxy 程式負責將請求傳送給域 B下的連結並獲取到資料,最後再通過 Proxy 將資料返回給頁面 JS 使用。
經過的訪問流程就是: 域 A 下 JS-- à 域 A 下 Proxy--- à 域 B 下的連結
例子:
第一步:
[javascript] view plaincopyprint?- <mce:script type=”text/javascript”><!--
- Var sUrl=” http://Jipiao.taobao.com/proxy.do ”; // 本域下代理地址
- var callback =
- {
- success: function(res) { alert(res.responseText); },
- failure: function
- argument:{}
- }
- YAHOO.util.Connect.asyncRequest(’GET’, sUrl, callback, null);
- // --></mce:script>
第二步:
Proxy 程式 ( 這裡假定是一個 servlet) :
[java] view plaincopyprint?- Public class Proxy extends …….{
- ..doGet(……..){
- HttpClient client=……;
- GetMethod get= new GetMethod(" www.baidu.com/xxxxx.do ") ;// 訪問域 B 的連結
- int statusCode = client.executeMethod( get );
- if (statusCode != HttpStatus.SC_OK) {
- byte[] responseBody = get.getResponseBody();
- String res=new String(responseBody);
- Httpresponse.getWriter().write(res);//
- 將資料返回給域
- A
- }
- }
- }
<!--[if !supportLists]-->② <!--[endif]--> Script 標籤 : 域 A 頁面 http://Jipiao.taobao.com/test.htm 的 head 中寫一個空的 Script 標籤
[xhtml] view plaincopyprint?- <html>
- <head>
- <mce:script id=”remoteScript” type=”text/javascript” src="””" mce_src="””" /><!--
- <head>
- <body>
- <script type=”text/javascript” >
- Var remoteScript=document.getElementById(‘remoteScript’);
- remoteScript.src=” www.baidu.com/xxxxx.do”;// 域 B 的連結
- alert(remote.test);// 使用域 B 返回的 JSON 資料
- alert(f[0]);
- // --></mce:script>
- </body>
- </html>
注意:這種方案要求域 B 返回的資料必須是合法的 JSON 格式或者如 JS 檔案的格式。
域 B 返回的資料格式如下:
[javascript] view plaincopyprint?- Var remote={test:’hello’};
- Var f=[‘2,1];
- {“test”:"hello","arrays":[2,1]}
對於基於同一父域的子域之間頁面的訪問這一類情況,還有第三種方式:
③ 隱藏 iframe: 即域 A jipiao.taobao.com/yyyy.htm 的頁面上寫一個隱藏的 iframe ,
- <html>
- <head>
- <head>
- <body>
- <mce:script type=”text/javascript” ><!--
- Document.domain=”taobao.com”;
- Var remoteHtml=document.getElementById(“remoteHtml”);
- remoteHtml.src=”promotion.taobao.com/xxxx.htm”;// 這裡訪問域 B 的連結
- var document=remoteHtml.ContentDocument;
- …// 這裡就可以使用 document 來操作域 B 中頁面 xxx.htm 的資料了
- // --></mce:script>
- <iframe id=”remoteHtml” src="””" mce_src="””" style="”diapay:none”/" mce_style="”diapay:none”/">
- </body>
- </html>
這裡 promotion.taobao.com/xxxx.htm 頁面也需要設定 document.domain="taobao.com" , 這種方法才能奏效。之所以這種 iframe 的方法不適合不同父域之間的跨域,是因為設定 document.domain 只能設定為自己的父域,而不是能設定為其他域,例如 :jiapiao.taobao.com 只能設定document.domain=”taobao.com” ,而不是是 document.domain=”baidu.com”
優缺點比較:
這裡列舉的三種方案各有優缺點:
Proxy 方案優點是可以適用用於幾乎所有的跨域訪問,而且只需要要一個域中進行開發,另一個域可以提供任何型別格式的資料。缺點是這種方案經過了中間 Proxy ,所以延遲可能稍微大一點,並且會加重本域伺服器的負荷,開發工作量也稍微大一點。
Script 標籤的方案可以說是非常簡單的,不用幾行程式碼就搞定了事,不過它對返回的資料格式要求有點嚴格,只能是 Json 格式資料 , 如果是其他格式的資料,那麼這種方法就無能為力了。
隱藏 iframe 方式也很簡單,它可以處理任何返回的資料格式,但它只適用在具有同一個父域下的跨域請求上,並且要求其他域得配合開發,即需要設定 document.domain
================
補充:Script標籤標籤方法最好要求服務端返回的json需要有控制代碼,即 如 json={...} 什麼的。因為客戶端需要使用這個控制代碼來引用。如果沒有,客戶端JS只有採用 var json=eval(jsonStr)方式來執行,效率不是很高。還有一種形式就是客戶端傳入要回調的方法。例如 xxxx.do?callbackApi=ca
服務端接收到callbackApi引數後,將json包裝在ca中,如:ca({.....});
客戶端定義回撥函式就可以訪問了。
function ca(json){
.......
}
不論如何,這種方法對於服務端都有一定耦合。