Ajax 跨域(CORS)请求中的预检请求

关于预检请求

定义:预检请求(Preflighted requests )是浏览器发起跨域请求时,经过OPTIONS方法询问服务器对跨域请求的支持状况(支持的包含请求方法、请求头、数据类型)。html

触发预检请求的三类条件json

  1. 默认状况下,跨域请求只支持GET,HEAD,POST方法,若是不是这三个请求方法(好比:PUT、DELETE、CONNECT、OPTIONS、TRACE和PATCH),那么将触发预检请求跨域

  2. 默认状况下,浏览器跨域请求时,会自动添加的请求头(HOST,Referer,Connection、Accept、User-Agent,Accept-Languange,Accept-Encoding,Accept-Charset和Content-Type),这些请求中还有其余请求头时,那么将触发预检请求。浏览器

  3. 如一、2所说的状况排除在外的条件下,跨域请求是,浏览器支持的Content-Type值为application/x-www-form-urlencoded,multipart/form-data和text/plain。若是是其余数据类型(如application/json,text/xml...),那么将触发预检请求。缓存

下面经过一个Ajax跨域请求来验证服务器

var xhr= new XMLHttpRequest();
var url = 'http://bar.other/resources/post-here/';
var body = '<?xml version="1.0"?><person><name>Arun</name></person>';
function callOtherDomain(){
  if(invocation)
    {
      xhr.open('POST', url, true);
      xhr.setRequestHeader('X-PINGOTHER', 'pingpong'); //自定义的Header
      xhr.setRequestHeader('Content-Type', 'application/xml'); //特殊的文档类型
      xhr.onreadystatechange = function(){};
      xhr.send(body); 
}
}

知足以上之一,即可发起预检请求,预检请求流程以下cookie

OPTIONS /resources/post-here/ HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081130 Minefield/3.1b3pre
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Connection: keep-alive
Origin: http://foo.example
Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-PINGOTHER, Content-Type


HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:39 GMT
Server: Apache/2.0.61 (Unix)
Access-Control-Allow-Origin: http://foo.example
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: X-PINGOTHER, Content-Type
Access-Control-Max-Age: 86400
Vary: Accept-Encoding, Origin
Content-Encoding: gzip
Content-Length: 0
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: text/plain

POST /resources/post-here/ HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081130 Minefield/3.1b3pre
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Connection: keep-alive
X-PINGOTHER: pingpong
Content-Type: text/xml; charset=UTF-8
Referer: http://foo.example/examples/preflightInvocation.html
Content-Length: 55
Origin: http://foo.example
Pragma: no-cache
Cache-Control: no-cache

<?xml version="1.0"?><person><name>Arun</name></person>


HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:40 GMT
Server: Apache/2.0.61 (Unix)
Access-Control-Allow-Origin: http://foo.example
Vary: Accept-Encoding, Origin
Content-Encoding: gzip
Content-Length: 235
Keep-Alive: timeout=2, max=99
Connection: Keep-Alive
Content-Type: text/plain

[Some GZIP'd payload]

 

咱们看到,首次发起的是OPTIONS请求,由于OPTIONS请求的做用自己就是询问服务器的请求,他这里询问浏览器是否支持以下条件请求app

Access-Control-Request-Method: POST  
#是否支持POST
Access-Control-Request-Headers: X-PINGOTHER, Content-Type  
#是否支持X-PINGOTHER自定义请求头的内容,Content-Type多是其余值,所以有必要询问

 

服务器回应是post

Access-Control-Allow-Origin: http://foo.example
#支持foo.example域
Access-Control-Allow-Methods: POST, GET, OPTIONS
#支持的方法POST,GET,OPTIONS,实际上OPTIONS自己就支持
Access-Control-Allow-Headers: X-PINGOTHER, Content-Type 
#支持的请求头,Content-Type是特殊的类型时,会触发预检,所以,这里最好加上Content-Type
Access-Control-Max-Age: 86400
#支持的缓存时间

若是知足以上条件,浏览器会自动发起提交以前没提交的数据,不然拒绝提交数据。url

 

关于withCredentials

在跨域请求中,相似Cookie等敏感信息通常不会跨域传输,可是在服务器容许的状况下,Cookie会被发送

注意:Cookie的发送须要服务器容许才行,此外,跨域js所处的环境必须是线上环境【服务器环境】

Access-Control-Allow-Credentials: true

浏览器也要容许

var xhr= new XMLHttpRequest();

var url = 'http://bar.other/resources/post-here/';
var body = '<?xml version="1.0"?><person><name>Arun</name></person>';

function callOtherDomain(){
  if(invocation)
    {
      xhr.withCredentials = true;#容许cookie信息
      xhr.open('POST', url, true);
      xhr.setRequestHeader('X-PINGOTHER', 'pingpong'); //自定义的Header
      xhr.setRequestHeader('Content-Type', 'application/xml'); //特殊的文档类型
      xhr.onreadystatechange = function(){};
      xhr.send(body); 
}
}

 

服务器设计建议

服务器设计,咱们不只要处理常见的跨域请求,对于Preflighted  Request请求,咱们更应该保证浏览器能获得预检请求的结果,所以,服务器端必定要处理OPTIONS请求

相关文章
相关标签/搜索