日期:2014-05-16  浏览次数:20895 次

AJAX跨域问题

??? 这几天被跨域问题困扰了很久,现在整理下思绪,以备不时之需。
??

???? 由于安全方面的考虑,JS被限制了跨域访问的能力,但是有时候我们希望能够做一些合理的跨域访问的事情,那么怎么办呢?


这里分两类情况:


一、基于同一父域的子域之间页面的访问;参见如下3个domain域:sina.com、news.sina.com、mail.sina.com;它们有相同的父域taobao.com。


二、基于不同父域页面之间的访问;参见如下3个domain域:taobao.com、baidu.com、sina.com.cn;它们具有不同的父域。


根据不同的跨域需求,跨域技术可以归为下面几类:

?


方案1:服务器Proxy


域A 的页面JS需要访问域B下的链接获取数据,该方案在域A的服务器端建立一个Proxy程序(可能是PHP\ASP、servlet等任何服务端程序),域A的页面 JS直接调用本域下的Proxy程序,proxy程序负责将请求发送给域B下的链接并获取到数据,最后再通过Proxy将数据返回给页面JS使用。


经过的访问流程就是: 域A下JS --> 域A 下Proxy -- > 域B下的链接


Proxy方案优点是可以适用用于几乎所有的跨域访问,而且只需要要一个域中进行开发,另一个域可以提供任何类型格式的数据。缺点是这种方案经过了中间Proxy,所以延迟可能稍微大一点,并且会加重本域服务器的负荷,开发工作量也稍微大一点。


方案2:隐藏iframe、共享domain :


通常情况下,我们会用与下面类似的代码来创建一个XMLHttpRequest对象:


factories = [

function() { return new XMLHttpRequest(); },

function() { return new ActiveXObject("Msxml2.XMLHTTP"); },

function() { return new ActiveXObject("Microsoft.XMLHTTP"); }

];

function newRequest() {

 for(var i = 0; i < factories.length; i++) {

   try{

    var factory = factories[i];

    return factory();

  } catch(e) {}

 }

 return null;

}



?

上面的代码中引用ActiveXObject,是为了兼容IE6系列浏览器。每次我们调用newRequest函数,就获得了一个刚刚创建的 Ajax对象,然后用这个Ajax对象来发送HTTP请求。例如,下面的代码向abc.example.com发送了一个GET请求:


var request = newRequest();

request.open("GET", "http://abc.example.com" );

request.send(null);


?

假设上面的代码包含在一个abc.example.com域名下的页面里,则这个GET请求可以正常发送成功,没有任何问题。然而,如果现在要向def.example.com发送请求,则出现跨域问题,JavaScript引擎抛出异常。


解决的办法是,在def.example.com域下放置一个跨域文件,假设叫crossdomain.html;然后将前面的 newRequest函数的定义移到这个跨域文件中;最后像之前修改document.domain值的做法一样,在crossdomain.html文件和 abc.example.com域下调用Ajax的页面顶端,都加上:


?

<script type="text/javascript">

document.domain = "example.com";

</script>


?

?

为了使用跨域文件,我们在abc.example.com域下调用Ajax的页面中嵌入一个隐藏的指向跨域文件的iframe,例如:

<iframe name="xd_iframe" style="display:none" src="http://def.example.com/crossdomain.html"></iframe>

这时abc.example.com域下的页面和跨域文件crossdomain.html都在同一个域(example.com)下,我们可以在abc.example.com域下的页面中去调用crossdomain.html中的newRequest函数:

var request = window.frames["xd_iframe"].newRequest();

这样获得的request对象,就可以向http://def.example.com发送HTTP请求了。


隐藏iframe方式也很简单,它可以处理任何返回的数据格式,但它只适用在具有同一个父域下的跨域请求上,并且要求其他域得配合开发,即需要设置document.domain。


方案3:
JSONP 跨域GET请求 通过Script标签) :


在域A页面http://