javascript中跨域请求详解

快乐打工仔 分类:实例代码

在很多时候,跨域请求是被拒绝的,这是因为安全性因素所决定的。

虽然实现了安全问题,但是却带来了一些不方便的因素,下面就介绍一下垮与请求相关内容。

一.什么是跨域:

假设a.com/get.html需要获取b.com/data.html中的数据,而这里a.com和b.com并不是同一台服务器,这就是跨域跨域会涉及到Javascript的同源策略,简单来说就是为了保护网站的安全,不被外域(非同源)服务器的js修改本网站内容。下面是跨域的一些情况罗列,图片如下:

前端教程

二.跨域解决方案:

(1).jsonp方式:

提到跨域不能不先提及jsonp。jsonp其实是JavacScript Object Notation with Padding的简称,可以理解成填充了内容的json格式数据。

因为以上声明了callback并且调用外域b.com的data.js,而data.js中调用:

callback({msg:"tqtan"});

这样当调用引用外域的js就会调用本地的callback()从而实现数据传输。

上面是只是简单的跨域,我们来看jQuery的真正的运用。

jQuery中的ajax可拉取外域的数据,通过两种方法:

(2).$.getJSON()方式:

这是一种比较简单的实现方式,当然是用起来简单,至于jquery内部怎么实现的暂时不关注:

$.getJSON("http://b.com/dataServlet?callback=?",function(data){
console.log(data.msg);
});

假设上述请求访问b.com下的servlet页面,传的参数为callback=?,jQuery会自动生成字符串填补占位符?,例如callback=jQuery17207481773362960666_1332575486681。

这就声明了与服务器的唯一标示,服务器只需要返回带有这个callback值的json格式数据即可,例如:

//dataServlet.java
String callback = req.getParameter("callback");
PrintWriter out = resp.getWriter();
out.print(callback+"('msg','tqtan')");

上面的方式就可以让我们成功取得服务器数据:

(3).$.ajax()方式:

实现原理和上面一样,只是可以自定义更多链接。

$.ajax({
  url:'http://b.com/dataServlet?words=hi',
  dataType:'jsonp',
  jsonp : 'jsoncallback',
  jsoncallback : 'tqtan',
  success:function(data){
    console.log(data.msg);
  },
  error: function (e) {
    console.log(e);
  }
});

可以自定义callback的名字,这里改为'tqtan',同时这里可以传值words=hi。

注意:JsonP格式只能是以GET形式请求服务器。

(4).document.domain方式:

这种方法只适用于主域相同,而子域不同的跨域。

也就是get.a.com和data.a.com的跨域问题,解决方法很简单:

若get.a.com/get.html需要获取data.a.com/data.html的数据,首先在get.html插入一个iframe,src指向data.a.com/data.html,然后在data.html写上document.domain='a.com';即可操纵data.html内的内容。

//get.html
var iframe = document.creatElement("iframe");
iframe.src="http://data.a.com/data.html";
iframe.style.display="none";
document.body.appendChild(iframe);
document.domain = 'a.com';
iframe.onload = function(){
  var otherDocument = iframe.contentDocument || iframe.contentWindow.document;
  //otherDocument就是另一个页面的document
  //do whatever you want..
};
//data.html
document.domain = 'a.com';

(5).url hash方式:

你也可以通过url的hash来实现跨域。

hash就是url#后面的内容,例如http://targetkiller.net/index.html#data,这里#data就是hash。

如何实现跨域:

还是那个例子,a.com/get.html需要获取b.com/data.html,首先在get.html建立一个iframe,src还是指向data.html,后面带上hash值实现传参。另一端data.html根据获取的hash作出响应,自身也创建一个iframe,src指向a.com/proxy.html,并把响应数据添加到hash。之后,a.com/proxy.html只需要修改在同一a.com父域的get.html的hash即可。最后,怎样获取数据呢?只需要在get.html写一个定时器setInterval,定期监听有无新的hash即可。

看到这里,你可能感到开始乱了,几个问题:

1).proxy.html的作用:

由于get.html和data.html不在一个域上,所以不能修改location.hash值,于是运用proxy.html,先跳到找个代理页面,然后通过parent.location.hash,也就是修改父亲,让儿子(get.html)也得到响应。

a.com/get.html

var iframe = document.createElement('iframe');
iframe.src = 'http://a.com/get.html#data';
iframe.style.display = 'none';
document.body.appendChild(iframe);
//周期检测hash更新
function getHash() {
  var data = location.hash ? location.hash.substring(1) : '';
  console.log(data);
}
var hashInt = setInterval(function(){getHash()}, 1000);
//a.com/proxy.html
parent.location.hash = self.location.hash.substring(1);
//b.com/data.html
//模拟一个简单的参数处理操作
if(location.hash){
  var data = location.hash;
  doSth(data);
}
function doSth(data){
  console.log("from a.com:"+data);
  var msg = "hello i am b.com";
  var iframe = document.createElement('iframe');
  iframe.src = "http://a.com/proxy.html#"+msg;
  iframe.style.display = 'none';
  document.body.appendChild(iframe);
}

(6).window.name方式:

这种方法比较巧妙,引用圆心的解释,name 值在不同的页面(甚至不同域名)加载后依旧存在,并且可以支持非常长的 name 值(2MB)。

具体例子依旧如上,同时也是需要一个代理页面。

a.com/get.html请求b.com/data.html,首先get.html创建一个iframe,src指向data.html,然后监听iframe的onload事件。

与此同时,在data.html设置window.name = data;把window.name赋值。

然后onload事件后马上把iframe的跳到本地a.com/proxy.html。

因此window.name就共享到了src为proxy.html的找个iframe中,接下来,就是同源间获取值的事了。

var state = 0,
iframe = document.createElement('iframe');
iframe.src = 'http://b.com/data.html';
iframe.style.display = 'none';
loadfn = function() {
  if (state === 1) {
    var data = iframe.contentWindow.name;
    console.log(data);
  } 
  else if (state === 0) {
    state = 1;
    //跳到proxy.html
    iframe.contentWindow.location = "http://a.com/proxy.html";
  }
};
if (iframe.attachEvent) {
  iframe.attachEvent('onload', loadfn);
} 
else {
  iframe.onload = loadfn;
}
document.body.appendChild(iframe);
a.com/proxy.html
// proxy.html的操作主要是删除get.html的iframe,避免安全问题发生
iframe.contentWindow.document.write('');
iframe.contentWindow.close();
document.body.removeChild(iframe);
b.com/data.html
var data = "hello,tqtan";
window.name = data;

(7).postMessage()方式:

html5的新方法postMessage()优雅地解决了跨域,也十分容易理解。

发送方调用postMessage()内容,接受方监听onmessage接受内容即可。

假设发送方为a.com/send.html,接受方为b.com/receive.html。

a.com/send.html

var iframe = document.createElement("iframe");
iframe.src = "http://b.com/receive.html";
document.body.appendChild(iframe);
iframe.contentWindow.postMessage("hello","http://b.com");
//b.com/receive.html
window.addEventListener('message', function(event){
  // 通过origin属性判断消息来源地址
  if (event.origin == 'http://a.com') {
    console.log(event.data);
    console.log(event.source);//发送源的window值
  }
}, false);

回复

我来回复
  • 暂无回复内容