CORS机制中,发送预检请求是一种保护机制,保护资源不被未授权的请求修改。
服务器基于预检请求头部信息来判断是否接受接下来的实际请求。和授权服务很像,预检请求通过,浏览器后续对同一服务的请求,不需要做跨域询问;服务端不想支持跨域访问,则啥也不用做。
预检请求头request header关键字段:
Access-Control-Request-Method
告诉服务器实际请求所使用的HTTP方法
Access-Control-Request-Headers
告诉服务器实际请求所携带的自定义首部字段,本次实际请求首部字段中content-type为自定义
预检响应头response header的关键字段:
Access-Control-Allow-Methods
返回服务端允许的请求,包含GET/HEAD/PUT/PATCH/POST/DELETE
Access-Control-Allow-Credentials
允许跨域携带cookie(跨域请求要携带cookie,必须设置为true)
Access-Control-Allow-Origin
允许跨域请求的域名,这个可以在服务端配置一些信任的域名白名单
Access-Control-Request-Headers
客户端请求所携带的自定义首部字段content-type
第2次请求
这是本来要发送的请求,如图所示是普通的post请求。
其中Content-Type的application/json是此次和后端约定的请求内容格式,这也是为什么会发送options请求的原因之一。
OPTIONS请求
使用OPTIONS方法对服务器发起请求,可以检测服务器支持哪些 HTTP 方法。但是我们并没有主动发起OPTIONS请求,OPTIONS请求为何会自动发起?
OPTIONS请求自动发起
MDN规范要求,对那些可能对服务器数据产生副作用的HTTP请求方法(特别是GET以外的HTTP请求,或者搭配某些MIME类型的POST请求),浏览器必须首先使用OPTIONS方法发起一个预检请求(preflight request),从而获知服务端是否允许该跨域请求。
所以这个跨域请求触发了浏览器自动发起OPTIONS请求,看看此次跨域请求具体触发了哪些条件。
优化OPTIONS请求:
Access-Control-Max-Age 或 避免触发
一旦达到触发条件,跨域请求便会一直发送2次请求,这样增加的请求数是否可优化?答案是可以,OPTIONS预检请求的结果可以被缓存。
Access-Control-Max-Age
这个响应首部表示preflight request(预检请求)的返回结果(即Access-Control-Allow-Methods和Access-Control-Allow-Headers提供的信息)可以被缓存的最长时间,单位是秒。
如果值为-1,则表示禁用缓存,即每一次请求都需要提供OPTIONS预检请求进行检测。
避免出现预检请求,需要使得你的请求满足简单请求的两个条件。
比如在使用JWT鉴权时,可能会把你的token放在请求头的authorization字段,因为这个字段超出了简单请求的范围,所以请求会变成非简单请求。这时可以不把token放在authorization请求头中。
尽量避免触发OPTIONS请求,上面例子中把content-type改掉是可以的。在其他场景,比如跨域并且业务有自定义请求头的话就很难避免了。现在使用的axios或者superagent等第三方ajax插件,如果出现CORS预检请求,可以看看默认配置或者二次封装是否规范。
出现预检请求后,进行服务器配置,分别设置好Access-Control-Allow-Origin、Access-Control-Allow-Methods和Access-Control-Allow-Headers,使得你的非简单请求能够通过预检请求。
如果使用Hapijs的话,只需要在路由配置中增加cors: true配置即可。