最近在项目中因前后端部署不同地方,前端在请求后端 api 时发生了跨域请求,我们采用
(跨域资源共享) 来解决跨域请求,这需要前后端的配合来完成。在这一过程中,后端支持了 CORS 跨域请求后,前端的请求配置可能会调起 CORS 的 preflight 请求,也就是我们所说的预检请求。对 CORS 不太熟悉的可能会很容易忽视掉这个问题。下面就来说说 CORS 的 preflight 请求。CORS 的基本用法不在本文讨论中,可以参考阮老师的 跨站资源共享 CORS 详解 。
- CORS
preflight 请求,就是在发生 cors 请求时,浏览器检测到跨域请求,会自动发出一个
请求来检测本次请求是否被服务器接受。一个 OPTIONS 请求一般会携带下面两个与 CORS 相关的头:
- OPTIONS
: 本次预检请求的请求方法。
- Access-Control-Request-Method
:本次请求所携带的自定义首部字段。这些字段是导致产生 OPTIONS 请求的一个原因。后面会讲到。
- Access-Control-Request-Headers
这样,服务端收到该预检请求后,会返回与 CORS 相关的响应头。主要会包括下面几个,但可能还会有其他的有关 CORS 字段:
: 服务器允许的跨域请求源
- Access-Control-Allow-Origin
: 服务器允许的请求方法
- Access-Control-Allow-Methods
: 服务器允许的自定义的请求首部字段
- Access-Control-Allow-Headers
服务器通过 CORS 跨域请求后,下面浏览器就会发生正式的数据请求。整个请求过程其实是发生了两次请求:一个预检请求,通过后的实际数据请求。这些都可以在浏览器网络请求中看到。可以参考下图:
需要注意的是:
1、在上面的两次请求中,预检请求只是一个检查的过程,它不会携带任何请求的参数;预检通过后的请求才会真正的携带请求参数与服务器进行数据通信。
2、若服务器对预检请求没有任何响应,那么浏览器不知道服务器是否支持 CORS 而不会发送后续的实际请求;或者服务器不支持当前的 Origin 跨域访问也不会发送后续请求。
上面的预检请求并不是 CORS 请求的必须的请求过程,在一定的条件下并不需要发生预检请求。那么发生预检请求的条件是什么呢?根据 HTTP 访问控制 (CORS) 介绍,其实发生预检请求的条件:是否是简单请求。简单请求则直接发送具体的请求而不会产生预检请求。具体来说如下:
满足下面的所有条件就不会产生预检请求,也就是该请求是简单请求:
、
- GET
、
- POST
其中任意一个
- HEAD
、
- text/plain
、
- multipart/form-data
中任意一个值
- application/x-www-form-urlencoded
满足上面所有的条件才不会发送预检请求,在实际项目中我们的请求格式可能是
格式编码,或者使用自定义请求头都会触发 CORS 的预检请求。
- application/json
所以,在项目中是否会触发 CORS 的预检请求要做到心中有数。
我们拿一个实际发生预检请求的例子来说明整个过程。考虑下面的一个例子:
- var xhr = new XMLHttpRequest();
- var url = 'http://bar.other/resources/post-here/';
- var body = '<?xml version="1.0"?><person><name>Arun</name></person>';
- function callCors(){
- if(xhr)
- {
- xhr.open('POST', url, true);
- xhr.setRequestHeader('X-PINGOTHER', 'pingpong');
- xhr.setRequestHeader('Content-Type', 'application/xml');
- xhr.onreadystatechange = handler;
- xhr.send(body);
- }
- }
- ......
上面请求中在请求中添加了自定义首部字段 X-PINGOTHER,并且请求的
值 application/xml。因此该请求首先会触发一个预检请求。具体的过程见下图
- Content-Type
通过上图可以看到请求实际产生了 2 次与服务交互的过程,最后一次会将请求参数传给服务器。这样一个 CORS 请求过程就完成了。
需要注意的一个有关 CORS 的点:
对于附带身份凭证的请求 (即服务器设置 Access-Control-Allow-Credentials: true),服务器不得设置 Access-Control-Allow-Origin 的值为 "*"。否则请求将会失败。
个人理解是 Cookie 还是遵循同源策略的,即使因为这个请求是跨域请求,所以每个 Origin 的 Cookie 是不能被其他 Origin 获取到的,也就是不允许 Access-Control-Allow-Origin 的值为 "*"。
1、 跨域资源共享 CORS 详解
2、 HTTP 访问控制(CORS)
3、 CORS - How do 'preflight' an httprequest?
来源: http://www.cnblogs.com/wonyun/p/CORS_preflight.html