首先要知道的是对于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你仅仅是知道出错了。
Event Handler | Description |
---|---|
onloadstart | 请求开始的时候 |
onprogress | 当前正在加载读取数据中 |
onerror | 请求失败 |
onload | 请求成功完成 |
ontimeout | 在请求完成之前超时了 |
注意:XHR以前只有一个onreadystatechange事件
在CORS中,当我们处理浏览器和服务器浏览器时,浏览器会添加一些额外的headers,还可能做一些额外的请求(浏览器自动发出)
我们可以通过抓包工具(例如fiddle)看到这些被隐藏起来的请求。
可以看到浏览器发送了preflight request
简单请求和非简单请求
简单请求就是
- HEAD
- GET
- POST
HTTP Headers 匹配下面的
简单的请求我们只需要用JSON-P或者HTML 表单post来请求即可,不需要用到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()方法默认只能访问下面这些简单的响应头:
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();
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。
Access-Control-Request-Method
指定实际请求的HTTP method,这个一定要有。
Access-Control-Request-Headers
发送HTTP OPTIONS方法试探真实请求是否能够安全发送,询问是否允许真实请求(actual request)。服务端会根据两个headers来验证HTTP method和真实请求的method是否有效。
有以下的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+