JS的跨域問題,我想很多程式員的腦海裡面還認為JS是不能跨域的,其實這是一個錯誤的觀點;
有很多人在網上找其解決方案,教其用IFRAME去解決的文章很多,真有那麼複雜嗎?
其實很簡單的,如果你用JQUERY,一個GETJSON方法就搞定了,而且是一行代碼搞定。
今天2013年8月2日又抽時間整理了下,修改了最佳化線上調用的方法。
我這裡提供了線上測試調用的功能,方便大家測試。點擊查看
其實跨域有兩種思路,思路一:就是通過js跨域訪問;思路二:是通過後台寫代碼訪問
下面說下兩種方法的實現:
思路一:通過js跨域訪問
一、伺服器端(遠端存取段),構造指定的json格式:
代碼如下 |
複製代碼 |
var url = "http://www.111cn.net /CsAjax.do?method=getCrossJson&jsoncallback=?" $.getJSON(url, {www_url:"www.111cn.net"}, function(json) { //返回格式: ?(json_data) /*返回資料: ?([{"www_url":"www.111cn.net","www_name":"www_name", "items":[{"p_name":"安徽省","p_index":340000},{"p_name":"北京市","p_index":110000}]}]) */ //調用執行個體:alert(json[0].www_url); }); |
注意:CsAjax.do?method=getCrossJson中,在輸出JSON資料時,一定要帶參數:jsoncallback,並將擷取的內容放到返回JSON資料的前面,假設實際擷取的值為Jquery123456_7890123,那麼返回的值就是 Jquery123456_7890123([{"www_url":"www.111cn.net","www_name":"www_name","items":[{"p_name":"安徽省","p_index":340000},{"p_name":"北京市","p_index":110000}]}]);
這個貼上我的遠程端的擷取代碼java寫的其他語言類似參考:
代碼如下 |
複製代碼 |
String www_url = (String) request.getAttribute("www_url"); String jsoncallback = (String) request.getAttribute("jsoncallback"); if (StringUtils.isBlank(www_url)) { www_url = "www.111cn.net"; } JSONObject jsonb = new JSONObject(); jsonb.put("www_url", www_url); jsonb.put("www_name", "愛森家園"); JSONArray items = new JSONArray(); JSONObject item = new JSONObject(); item.put("p_name", "安徽省"); item.put("p_index", 340000); items.put(item); jsonb.put("p_name", "北京市"); jsonb.put("p_index", 110000); items.put(item); jsonb.put("items", items); String json = jsoncallback + "([" + jsonb.toString() + "])"; if (StringUtils.isNotBlank(jsoncallback)) { //將特殊構造的資料:json 返回到頁面 } else { //將正常的資料jsonb返回到頁面 } |
因為getJSON跨域的原理是把?隨機變一個方法名,然後返回執行的,實現跨域響應的目的。
二、用戶端實際調用, 下面一個是跨域執行的真執行個體子(可跨所有網域名稱):
代碼如下 |
複製代碼 |
<script src="/scripts/jquery.js" type="text/javascript"></script> <script type="text/javascript"> $.getJSON("http://www.111cn.net /CsAjax.do?method=getCrossJson&jsoncallback=?", {www_url:"www.111cn.net"}, function(json) { alert(json[0].www_url); alert(json[0].www_name); alert(json[0].items[0].p_name); }); </script> |
後台寫代碼訪問
第一種思路有一個缺陷:就是如果需要訪問的服務端你無法控制的話,那麼你也就無計可施了,所以提供第二種思路,後台通過HttpClient 和 HttpGet 直接存取,
然後在後台擷取訪問的資料,在做處理,返回到頁面即可。
這個可以參考我的文章:有道翻譯 使用
jQuery跨域原理
瀏覽器會進行同源檢查,這導致了跨域問題,然而這個跨域檢查還有一個例外那就是HTML的<Script>標記;我們經常使用<Script>的src屬性,指令碼靜態資源放在獨立網域名稱下或者來自其它網站的時候這裡是一個url;這個url 響應的結果可以有很多種 , 比如 JSON, 返回的 Json 值成為 <Script> 標籤的 src 屬性值 . 這種屬性值變化並不會引起頁面的影響 . 按照慣例,瀏覽器在 URL 的查詢字串中提供一個參數,這個參數將作為結果的首碼一起返回到瀏覽器 ;
看下面的例子:
代碼如下 |
複製代碼 |
<script type="text/javascript" src="http://domain2.com/getjson?jsonp=parseResponse"> </script>
|
響應值:parseResponse({"Name": "Cheeso", "Rank": 7})
這種方式被稱作 JsonP ;(如果連結已經失效請點擊這裡: JSONP ) ;即:JSON with padding 上面提到的首碼就是所謂的“padding”。 那麼 jQuery 裡面是怎麼實現的呢?
貌似並沒有 <Script> 標記的出現!? OKay ,
頁面調用的是getJSON:
代碼如下 |
複製代碼 |
getJSON: function ( url, data, callback ) { return jQuery.get(url, data, callback, " json " ); },
|
繼續跟進
代碼如下 |
複製代碼 |
get: function ( url, data, callback, type ) { // shift arguments if data argument was omited if ( jQuery.isFunction( data ) ) { type = type || callback; callback = data; data = null ; } return jQuery.ajax({ type: " GET " , url: url, data: data, success: callback, dataType: type }); |
跟進 jQuery.ajax,下面是 ajax 方法的程式碼片段:
代碼如下 |
複製代碼 |
// Build temporary JSONP function if ( s.dataType === " json " && (s.data && jsre.test(s.data) || jsre.test(s.url)) ) { jsonp = s.jsonpCallback || ( " jsonp " + jsc ++ ); // Replace the =? sequence both in the query string and the data if ( s.data ) { s.data = (s.data + "" ).replace(jsre, " = " + jsonp + " $1 " ); } s.url = s.url.replace(jsre, " = " + jsonp + " $1 " ); // We need to make sure // that a JSONP style response is executed properly s.dataType = " script " ; // Handle JSONP-style loading window[ jsonp ] = window[ jsonp ] || function ( tmp ) { data = tmp; success(); complete(); // Garbage collect window[ jsonp ] = undefined; try { delete window[ jsonp ]; } catch (e) {} if ( head ) { head.removeChild( script ); } }; } if ( s.dataType === " script " && s.cache === null ) { s.cache = false ; } if ( s.cache === false && type === " GET " ) { var ts = now(); // try replacing _= if it is there var ret = s.url.replace(rts, " $1_= " + ts + " $2 " ); // if nothing was replaced, add timestamp to the end s.url = ret + ((ret === s.url) ? (rquery.test(s.url) ? " & " : " ? " ) + " _= " + ts : "" ); } // If data is available, append data to url for get requests if ( s.data && type === " GET " ) { s.url += (rquery.test(s.url) ? " & " : " ? " ) + s.data; } // Watch for a new set of requests if ( s.global && ! jQuery.active ++ ) { jQuery.event.trigger( " ajaxStart " ); } // Matches an absolute URL, and saves the domain var parts = rurl.exec( s.url ), remote = parts && (parts[ 1 ] && parts[ 1 ] !== location.protocol || parts[ 2 ] !==location.host); // If we're requesting a remote document // and trying to load JSON or Script with a GET if ( s.dataType === " script " && type === " GET " && remote ) { var head = document.getElementsByTagName( " head " )[ 0 ] || document.documentElement; var script = document.createElement( " script " ); script.src = s.url; if ( s.scriptCharset ) { script.charset = s.scriptCharset; } // Handle Script loading if ( ! jsonp ) { var done = false ; // Attach handlers for all browsers script.onload = script.onreadystatechange = function () { if ( ! done && ( ! this .readyState || this .readyState === " loaded " || this .readyState === " complete " ) ) { done = true ; success(); complete(); // Handle memory leak in IE script.onload = script.onreadystatechange = null ; if ( head && script.parentNode ) { head.removeChild( script ); } } }; } // Use insertBefore instead of appendChild to circumvent an IE6 bug. // This arises when a base node is used (#2709 and #4378). head.insertBefore( script, head.firstChild ); // We handle everything using the script element injection return undefined; }
|
上面的代碼第1行到第10行:判斷是JSON類型調用,為本次調用建立臨時的JsonP方法,並且添加了一個隨機數字,這個數字源於用日期值;
這個地方也就是Taven.李錫遠所說的“隨機變一個方法名”;
關注第14行,這一行相當關鍵,註定了我們的結果最終是<Script> ;然後是構造Script片段,第95行在Head中添加該片段,修成正果;
不僅僅是jQuery,很多js架構都是用了同樣的跨域方案,:)說到這裡,嗯,這就是getJSON跨域的原理,趙本山說了“情況呢就是這麼個情況”