JS的跨域问题,我想很多程序员的脑海里面还认为JS是不能跨域的,其实这是一个错误的观点;
有很多人在网上找其解决方法,教其用IFRAME去解决的文章很多,真有那么复杂吗?
其实很简单的,如果你用JQUERY,一个GETJSON方法就搞定了,而且是一行代码搞定。
今天2013年8月2日又抽时间整理了下,修改了优化在线调用的方法。
其实跨域有两种思路,
思路一:就是通过js跨域访问;思路二:是通过后台写代码访问
下面说下两种方法的实现:
一、服务器端(远程访问段),构造指定的json格式:
var url = "http://www.aiisen.com/CsAjax.do?method=getCrossJson&jsoncallback=?" $.getJSON(url, {www_url:"www.aiisen.com"}, function(json) { //返回格式: ?(json_data) /*返回数据: ?([{"www_url":"www.aiisen.com","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.aiisen.com","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.aiisen.com"; } 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跨域的原理是把?随机变一个方法名,然后返回执行的,实现跨域响应的目的。
具体getJSON的使用说明,请参考JQUERY手册。
二、客户端实际调用, 下面一个是跨域执行的真实例子(可跨所有域名):
<script src="http://www.aiisen.com/commons/scripts/jquery.js" type="text/javascript"></script> <script type="text/javascript"> $.getJSON("http://www.aiisen.com/CsAjax.do?method=getCrossJson&jsoncallback=?", {www_url:"www.aiisen.com"}, 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 });
// 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跨域的原理,赵本山说了“情况呢就是这么个情况”