CORS正确使用姿势

来源:互联网 发布:手机淘宝移动端 编辑:程序博客网 时间:2024/04/29 15:27

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 Handler Description 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.1Origin:http://api.bob.comHost: api.bob.com

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

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

Access-Control-Allow-Origin: http://api.bob.comAccess-Control-Allow-Credentials: trueAccess-Control-Expose-Headers: FooBarContent-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.1Origin: http://api.bob.comAccess-Control-Request-Method: PUTAccess-Control-Request-Headers: X-Custom-HeaderHost: api.alice.comAccept-Language: en-USConnection: keep-aliveUser-Agent: Mozilla/5.0...

Preflight Response Header

Access-Control-Allow-Origin: http://api.bob.comAccess-Control-Allow-Methods: GET, POST, PUTAccess-Control-Allow-Headers: X-Custom-HeaderContent-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.1Origin: http://api.bob.comHost: api.alice.comX-Custom-Header: valueAccept-Language: en-USConnection: keep-aliveUser-Agent: Mozilla/5.0...

响应体

Access-Control-Allow-Origin: http://api.bob.comContent-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+

0 0