定义:预检请求(Preflighted requests )是浏览器发起跨域请求时,经过OPTIONS方法询问服务器对跨域请求的支持状况(支持的包含请求方法、请求头、数据类型)。html
触发预检请求的三类条件:json
默认状况下,跨域请求只支持GET,HEAD,POST方法,若是不是这三个请求方法(好比:PUT、DELETE、CONNECT、OPTIONS、TRACE和PATCH),那么将触发预检请求跨域
默认状况下,浏览器跨域请求时,会自动添加的请求头(HOST,Referer,Connection、Accept、User-Agent,Accept-Languange,Accept-Encoding,Accept-Charset和Content-Type),这些请求中还有其余请求头时,那么将触发预检请求。浏览器
如一、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
在跨域请求中,相似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请求