AJAX跨域获取数据

3 篇文章 0 订阅
1 篇文章 0 订阅
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上的数据(这些限制使得下面的方式不被视为一种严重的安全漏洞)。具体做法是:
  1. 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页面上实现的函数的函数名。
  2. script允许跨域访问,a.com会向b.com申请这个url对应的js文件(代码)。b.com拿到这个url,取出callback参数,获得"fun"这个字符串。
  3. b.com动态生成js文件(代码),其中插入一段代码"fun(...)"。参数通常是一个JSON对象(这是惯例,a.com和b.com也可以协定别的参数类型),比如"fun({p1:1,p2:2})"。b.com将包含这句代码的js文件(代码)发送给a.com。
  4. 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, function(i,item){
        $("p").append(i+": "+item+"<br>");
      });
    }
);
注意这里调用的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调用会调用到用户自己编写的匿名函数。

[小贴士]

IE内核的浏览器在使用script.src方式请求资源时,request不会携带sessionid信息,对于服务器端而言,每次请求都来自不同的客户端,则在ajax轮询的场景里,服务器端可能无法准确判断客户端的身份而导致需求难以实现。FireFox、Chrome等浏览器没有这样的问题(它们的请求可以携带sessionid)。

解决办法是:客户端在script.src的请求url中加入特殊的参数以携带sessionid(类似上面的callback参数,与b.com做好约定),如果客户端还未获取sessionid,这个参数可以不指定;服务器获取这个sessionid,通过它来判断客户端的身份,如果url中没有指定,则使用当前自动生成的sessionid(request.getSession().getId()),并把它加入返回的json对象的某个字段中;客户端获取这个字段的值,并把它加入下次请求的url中。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值