跨域请求方法

什么是跨域

由于浏览器同源政策。当前页面发起的请求的路径中,只要协议,域名,或者端口其中之一与本页面的路径不相同,就是跨域。

跨域方法

但是,因为业务需求,我们必须要进行跨域。跨域方法:

JSONP

先说说大家最熟悉的跨域解决方法JSONP。JSONP实际上使用了浏览器不会限制script和img跨域的特性。代码如下:

1
2
3
4
var script = document.createElement('script');
script.src= 'http://alg.api.xxx.com/search/suggest?jsonpcallback=jQuery17109022109293184162_1507254711209&type=1&kw=so&size=10&_=1507255363712'; //xxx为zbj,防止公司安全的同事找上门
script.type = "text/javascript"
document.body.appendChild(script);

缺点:

  1. 安全问题,JSONP导致其他非公司网站也能使用这个请求。
  2. 无法发送POST请求。(这也是我之前某个项目里放弃JSONP的原因)。

CORS 跨域资源共享

CORS需要服务端和客户端相互配合,客户端请求和普通的ajax请求一样。
客户端请求分为2种,一种简单请求,另外一种预先请求。
缺点:

  1. 兼容问题,IE下有些不兼容。
  2. 需要后端配合,不利于前后端独立。

简单请求

请求方法是下列之一:GET,POST,HEAD
请求头是下列之一:application/x-www-form-urlencoded,multipart/form-data,text/plain。
上面这几种请求属于简单请求,当跨域时,浏览器会自动在头部中添加Origin。

1
2
3
4
5
6
7
POST /search HTTP/1.1
Accept-Encoding:gzip, deflate
Accept-Language:zh-CN,zh;q=0.8,en;q=0.6
Connection:keep-alive
Content-Length:7237
Content-Type:application/x-www-form-urlencoded
Origin: http://search.XXX.com //xxx为zbj,防止公司安全的同事找上门

服务器端接受到请求后,通过自身需求处理,返回带有Access-Control-Allow-Origin和
Access-Control-Allow-Methods的响应头。

1
2
3
Access-Control-Allow-Credentials:true //服务器允许cookie包含在请求中
Access-Control-Allow-Headers:Content-Type //服务器支持的所有头信息字段
Access-Control-Allow-Methods:POST, GET //服务器支持的所有跨域请求的方法

预先请求

当请求条件超出简单请求的条件时,为预先请求。预先请求会发起2个请求。预先请求不会直接发起
对应的数据请求,而是首先发起OPTION请求,询问服务器是否允许当前域名页面发起跨域请求,跨域
方法是什么等。最后,得到浏览器认可后,才发起数据请求。
OPTIONS请求:

1
2
3
4
OPTIONS /receive HTTP/1.1
Access-Control-Request-Headers:content-type,x-requested-with
Access-Control-Request-Method:POST
Origin:http://www.xxx.com //xxx为zbj

response:

1
2
3
4
Access-Control-Allow-Headers:x-requested-with,content-type
Access-Control-Allow-Methods:POST, GET, OPTIONS
Access-Control-Allow-Origin:*
Access-Control-Max-Age:3600

数据请求:

1
2
3
4
POST /receive HTTP/1.1
Content-type:application/json; charset=UTF-8
Origin:http://www.xxx.com
...

response:

1
2
3
4
Access-Control-Allow-Headers:x-requested-with,content-type
Access-Control-Allow-Methods:POST, GET, OPTIONS
Access-Control-Allow-Origin:*
Access-Control-Max-Age:3600

CORS通常是不携带cookie请求和HTTP认证信息。如果需要携带,需要设置withCredentials属性为true。

1
2
3
4
5
Jquery:
xhrFields: {withCredentials: true}
Javascript:
var xhr = new XMLHttpRequest();
xhr.withCredentials = true;

服务端设置Access-Control-Allow-Credentials属性为true。当携带cookie时,Access-Control-Allow-Origin
不能为星号。

document.domain + iframe

通过在页面中嵌入iframe的方式。
父页面

1
2
3
4
5
6
7
8
document.domain = "example.com";
var iframe = document.createElement('iframe');
iframe.src = 'http://www.xxx.example.com/子.html';
document.body.appendChild(iframe);
iframe.onload =function(){
var iframedocument = iframe.contentDocument || iframe.contentWindow.document;
...
}

子页面

1
document.domain = "example.com";

缺点:

  1. 只支持主域名相同的页面。
  2. 子页面还得设置domain,很多场景确实都不适合。

postMessage

postMessage是一个HTML5的新api,提供了允许不同源的脚本之间相互通讯。
postMessage(data,origin)参数
data:传递的数据,HTML5中规定数据为任意类型。但是为了支持各种浏览器,数据最好为字符串。
origin:字符串参数,目标窗口的源,协议+主机+端口号。也可以设置为星号。
父页面

1
2
3
4
5
6
var iframe = document.createElement('iframe');
iframe.src = 'http://www.example.com/子.html';
document.body.appendChild(iframe);
iframe.onload = function(){
iframe.contentWindow.postMessage('hello,world','http://www.example.com');//发送信息
}

子页面

1
2
3
4
5
window.addEventListener('message',function(event){
console.log(event.data);//数据 hello,world
console.log(event.origin) //数据发送端的源
console.log(event.source); // 数据发送端的window对象, 同源才能使用,跨域没办法
}

兼容IE XDomainRequest

IE8,IE9提供了XDomainRequest来解决跨域问题。

1
2
3
4
5
6
7
8
var xdr = new XDomainRequest();
xdr.open('get', 'http://www.example.com');
xdr.send({data:1});
xdr.timeout = 5000;
xdr.onload = function(){
console.log(xdr.responseText);
}
...

缺点:

  1. 只支持POST,GET请求。
  2. 协议不同的跨域无法支持。
  3. 无法携带cookie等。

参考:
XDomainRequest object