您的位置:首页 > 理论基础 > 计算机网络

CORS正确使用姿势

2017-02-25 20:36 183 查看

CORS用法

首先要知道的是对于CORS,我们要用到XHR2,然后IE的话是XDomainRequest Object,它从IE10开始才是XHR2. IE 8 9都是XDomainRequest

首先需要写一个处理兼容性的函数,确定是哪个浏览器上运行

function createCORSRequest(method,url){
var xhr=new XMLHttpRequest();
if("withCredentials" in xhr)    {
//Check if the XMLHttpRequest object has a "withCredentials" property
//this property only exists on XHR 2 object

}else if(typeof XDomainRequest!='undefined'){
//otherwise check if XDomainRequest
//XDomainRequest only exists in IE
xhr =new XDomainRequest();
xhr.open(method,url);
}else{
//othewise CORS is not supported by the browser
xhr=null;
}
return xhr;
}


该函数先检测XHR对象是否有withCredentials属性,这个属性只有XHR2的对象才有。

然后检测XDomainRequest是否存在。

实际的请求处理代码

function makeCORSRequest(){
//
var url='';
var xhr=createCORSRequest('GET',url);
if(!xhr){
throw new Error('CORS not supported');
return ;
}
//Reponse Handlers
xhr.onload=function(){
var text=xhr.responseText;
var title=getTitle(text);
console.log('Response from CORS request to ' + url + ': ' + title);
};

xhr.onerror=function(){
alert('Woops, there was an error making the request.');
};

xhr.send(); //send(data)
}


我们做了一个简单的GET请求,如果xhr不存在,即这是个不支持CORS的浏览器,就抛出这个错误并返回。

然后在onload(成功请求并返回)的事件中处理我们的业务,在onerror里面是处理请求过程出错的业务。

注意

浏览器对于出错的report不是做的很好,比如FF仅仅report status 0 和一个空的statusText for all errors! 浏览器可能会console.log输出信息,但是这些信息不能被js访问到!!! 所以handle onerror你仅仅是知道出错了。

XHR2新增的事件及其含义

Event HandlerDescription
onloadstart请求开始的时候
onprogress当前正在加载读取数据中
onerror请求失败
onload请求成功完成
ontimeout在请求完成之前超时了
注意:XHR以前只有一个onreadystatechange事件

添加CORS支持到服务器

在CORS中,当我们处理浏览器和服务器浏览器时,浏览器会添加一些额外的headers,还可能做一些额外的请求(浏览器自动发出)

我们可以通过抓包工具(例如fiddle)看到这些被隐藏起来的请求。



可以看到浏览器发送了preflight request

CORS请求类型

简单请求和非简单请求

简单请求就是

- HEAD

- GET

- POST

HTTP Headers 匹配下面的

Accept

Accept-Language

Content-Language

Last-Event-ID

Content-Type, 但值是其中的一种:

application/x-www-form-urlencoded

multipart/form-data

text/plain

简单的请求我们只需要用JSON-P或者HTML 表单post来请求即可,不需要用到CORS。

处理CORS和同源的原理

注意的是一个有效的CORS请求,总是包含了一个“Origin” header,这是被浏览器自动附加的,但是这个并不是表明这个请求一定是跨域请求。 一些浏览器的同源请求也会有这个header。

一个HTTP 请求例子:

POST /cors HTTP/1.1
Origin:http://api.bob.com
Host: api.bob.com


不过浏览器会expect CORS的响应头,如果是跨域的情况下,同源的时候,它不会理会是否有CORS 的headers。

一个有效的服务器CORS响应:

Access-Control-Allow-Origin: http://api.bob.com Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: FooBar
Content-Type: text/html; charset=utf-8


详解介绍这些响应的意义

Access-Control-Allow-Origin(required)

这个header必须included,所有有效的CORS响应都必须要有。 它的值可以设置成特定的origin 或者 是 “*”,一般也就是你请求脚本所在的源。

Access-Control-Allow-Credentials(optional)

默认,cookie是不会在CORS请求中被附加的,使用这个头,设置成true表示客户端可以附加cookie到CORS请求。 如果你不需要cookie就不用设置。

不要设置成false!!!!

如果在你的JS中设置了XHR 2 对象的widthCredentials属性,这个属性也都必须设置成true才能成功。

两个都必须有

另外附加说明:

这些跨域设置的cookie,也遵循同源策略,你的JS不能通过document.cookies or response headers访问这些cookies

只能被remote domain (即远端服务器)所控制。

Access-Control-Expose-Headers (optional)

XHR2 对象有一个getResponseHeader()方法,返回特定的响应头。 当一个CORS请求时,getResponseHeader()方法默认只能访问下面这些简单的响应头:

Cache-Control

Content-Language

Content-Type

Expires

Last-Modified

Pragma

如果你要访问其他header,就要用这个Access-Control-Expose-Headers

如果一个不那么简单的请求,实际包括两个请求

一个preflight request和一个actual request

JS代码

var url = 'http://api.alice.com/cors';
var xhr = createCORSRequest('PUT', url);
xhr.setRequestHeader(
'X-Custom-Header', 'value');
xhr.send();


HTTP请求响应如下



Preflight Reqeust Header

OPTIONS /cors HTTP/1.1
Origin: http://api.bob.com Access-Control-Request-Method: PUT
Access-Control-Request-Headers: X-Custom-Header
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...


Preflight Response Header

Access-Control-Allow-Origin: http://api.bob.com Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: X-Custom-Header
Content-Type: text/html; charset=utf-8


preflight请求是一个HTTP OPTIONS 请求,你的服务器要能够响应这种method。

它也包含了一些专有的request heaer

Access-Control-Request-Method

指定实际请求的HTTP method,这个一定要有。

Access-Control-Request-Headers

Preflight Request的作用

发送HTTP OPTIONS方法试探真实请求是否能够安全发送,询问是否允许真实请求(actual request)。服务端会根据两个headers来验证HTTP method和真实请求的method是否有效。

对于Preflight response

有以下的response header:

Access-Control-Allow-Origin

同样是必须提供的

Access-Control-Allow-Methods

允许的HTTP 方法,这个响应头可以包含服务器支持的所有HTTP方法。 因为preflight response可能被cached缓存,所以一个简单preflight response可以包含很多细节关于多种请求类型。

Access-Control-Allow-Headers

如果请求有一个 Access-Control-Request-Headers header的header就需要这个响应头~

可以返回所有服务器支持的响应头。

Access-Control-Max-Age

这个响应头可以使得每个preflight请求变得昂贵。

因为浏览器时做了两个请求weighted每个客户端请求。这个响应头可以让响应被缓存(cached)一个特定的时间。

我们来看真实(实际)请求

请求体

PUT /cors HTTP/1.1
Origin: http://api.bob.com Host: api.alice.com
X-Custom-Header: value
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...


响应体

Access-Control-Allow-Origin: http://api.bob.com Content-Type: text/html; charset=utf-8


我们可以看到真实请求中,我们的方法是put,而响应少了很多header,因为前面的preflight响应过了。

服务器如何拒绝

如果服务器要拒绝CORS请求,直接返回一个没有CORS header的普通响应即可。

有时服务器想要拒绝请求如果在preflight请求中的HTTP method 或者首部(headers)是无效的。

浏览器接收到响应之后,发现没有CORS的headers,就不会做一个真实(实际)的请求。

兼容性

Chrome 3+

Firefox 3.5+

Opera 12+

Safari 4+

Internet Explorer 8+
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  xmlhttprequest CORS