AJAX跨域获取数据
ajax可以在不刷新页面的情况下,获取服务器端数据。不过由于各浏览器的安全限制,ajax很可能不允许跨域访问(这类限制是必要的,如果不加任何限制,ajax可以通过"file://xxx"获取本地主机的一些重要信息并发送往服务器,造成安全隐患)。
但有的时候,位于a域(假设域名为a.com)的页面确实需要访问b域(假设域名为b.com)并获取一些数据,有没有办法实现这样的需求呢?
传统的方式是在a域的服务器端处理,即客户端访问a.com的一个页面,a.com在服务器端通过http协议访问b域,获取数据后返回给客户端。相当于a.com服务器充当了代理。
这种方式的缺点是,每次访问a.com的这个页面,都会造成整页刷新。下面的场景不太好处理:b.com对请求的处理比较耗时,它允许客户(这里的客户包括a.com和浏览器客户端等)的请求立即返回,但后续需要不断轮询以查询处理进度。由于B-S架构不支持服务器端主动向客户端发消息,使用ajax轮询来模拟这种行为是很常见的。如果采用刚才提到的方法(a.com服务器做代理),每次轮询都造成整页刷新,用户体验不好。最好的解决方案还是在ajax中直接访问b.com。
ajax不能跨域访问,也就是说,a.com服务器生成的页面,不能通过ajax直接访问b.com的资源。有一种巧妙的办法绕过这种安全机制:
我们注意到,HTML的script标签,src属性可以跨域制定js文件的来源,即下面的代码是合法的
<script src="http://b.com/xxx.js"></script>
这段代码即使放到a.com生成的页面中,也能正确的加载到b.com中的js文件,并执行里面的代码。
如果a.com和b.com的开发者可以相互交流,或是b.com明确支持下面提到的约定,ajax可以通过这样的机制,获取b.com上的数据(这些限制使得下面的方式不被视为一种严重的安全漏洞)。具体做法是:
a.com的页面动态生成一个script元素,src属性设置为b.com上获取数据的url,比如是http://b.com/xxx?xxx,同时,在这个资源url最后加上一个特殊的参数,变成http://b.com/xxx?xxx&callback=fun。注意,这里的callback是个约定名称(在b.com的服务器端要特别处理callback名字的参数)。也就是说,a.com的页面中可以将这个参数名改成mycallback等任意的名字,但必须告诉b.com这个名字到底是什么。callback的值"fun"是一个a.com页面上实现的函数的函数名。
script允许跨域访问,a.com会向b.com申请这个url对应的js文件。b.com拿到这个url,取出callback参数,获得"fun"这个字符串。
b.com动态生成js文件,其中插入一段代码"fun(...)"。参数通常是一个JSON对象(这是惯例,a.com和b.com也可以协定别的参数类型),比如"fun({p1:1,p2:2})"。b.com将包含这句代码的js文件发送给a.com。
a.com的script标签获取到来自b.com的js文件后,立即执行,这时,会执行到"fun({p1:1,p2:2})"这句代码。注意到fun是a.com页面中的一个函数,则这里是个函数调用。此时a.com的fun函数就可以接收到b.com传过来的JSON对象({p1:1,p2:2})了。
JQuery也是使用类似的机制实现的JSONP,引用官方的例子如下,客户端
$.getJSON("http://api.flickr.com/services/feeds/photos_public.gne?tags=cat&tagmode=any&format=json&jsoncallback=?",
function(data){
$.each(data.items, function(i,item){
$("<img/>").attr("src", item.media.m).appendTo("#images");
if ( i == 3 ) return false;
});
}
);
注意这里调用的url中包含"jsoncallback=?","?"会被JQuery替换成实际的回调函数名,用户(网页编写者)不用关心。跨域服务器响应时,JQuery保证能调用到用户编写的匿名函数(例子中$.getJSON()的第二个参数)。
服务器端如果是JSP,代码大致如下
...
String jsoncallback=request.getParameter("jsoncallback");
...
PrintWriter out = response.getWriter();
out.print(jsoncallback+"({\"account\":\"XX\",\"passed\":\"true\",\"error\":\"null\"})");
Jquery取得的数据可能如下
JQUET0988788({"account":"XX","passed":"true","error":"null"})
JQUET0988788调用会调用到用户自己编写的匿名函数
摘自:intimater的专栏
补充:Web开发 , 其他 ,