最近在项目偶然发现使用第三方文件管理系统上传文件时,上传请求会发送两次。发现第一个是预检请求OPTIONS
。
老早就知道HTTP请求方法有OPTIONS
,但从来没使用过,一时引起了兴趣,遂查阅了相关资料。
什么是OPTIONS请求
OPTIONS
请求也被称为预检请求,主要用于获知服务端支持的HTTP请求方法。跨域资源共享(CORS
)标准新增了一组 HTTP 首部字段,配合预检请求可获知服务器允许哪些源站通过浏览器有权限访问哪些资源。
请求头
在OPTIONS
的请求头中主要有几个关键字段:Access-Control-Request-Method
、Access-Control-Request-Headers
、Origin
。
请求头字段 | 作用 |
---|---|
Access-Control-Request-Method | 告知服务器实际请求所使用的HTTP 方法 |
Access-Control-Request-Headers | 告知服务器实际请求所携带的自定义首部字段 |
Origin | 发起请求的源站 |
在本次请求中,实际采用的post请求,自定义头部字段有uthorization
,login-type
,x-requested-with
,如下图所示:
响应头
在OPTIONS
的响应头中比较重要的有五个字段:Access-Control-Allow-Methods
、Access-Control-Allow-Credentials
、Access-Control-Allow-Origin
、Access-Control-Request-Headers
、Access-Control-Request-Headers
响应头字段 | 作用 |
---|---|
Access-Control-Allow-Methods | 服务端允许的请求方法,包括GET /HEAD /PUT /PATCH /POST /DELETE |
Access-Control-Allow-Credentials | 允许跨域携带cookie (跨域请求时XMLHttpRequest.withCredentials 为true ,预检时响应头该字段必须设置为true ,才能携带cookie ) |
Access-Control-Allow-Origin | 允许跨域请求的域名,自行在服务端配置一些信任的域名白名单 |
Access-Control-Request-Headers | 客户端请求所携带的自定义首部字段content-type |
Access-Control-Max-Age | 本次预检请求的有效期,单位为秒,在此期间不用发出另一条预检请求。可用于优化 |
在本次请求中,其响应头如下:
预检请求的自动发起
除主动发起预检请求外,在跨域请求时经常会遇到自动发起预检请求。实际上在MDNCORS功能描述中有这样写到:
规范要求,对那些可能对服务器数据产生副作用的
HTTP
请求方法(特别是GET
以外的 HTTP 请求,或者搭配某些 MIME 类型的POST
请求),浏览器必须首先使用 OPTIONS 方法发起一个预检请求(preflight request
),从而获知服务端是否允许该跨域请求。服务器确认允许之后,才发起实际的 HTTP 请求。
当发生以下情况时,客户端就会自动发起OPTIONS预检请求
- 使用以下任一HTTP 方法:
PUT
/DELETE
/CONNECT
/OPTIONS
/TRACE
/PATCH
- 设置了以下头部字段以外的自定义字段:
Accept/Accept-Language
/Content-Language
/Content-Type
/DPR
/Downlink
/Save-Data
/Viewport-Width
/Width
Content-Type
的值不属于下列之一:application/x-www-form-urlencoded
、multipart/form-data
、text/plain
- 请求中的任意
XMLHttpRequestUpload
对象均没有注册任何事件监听器;XMLHttpRequestUpload
对象可以使用XMLHttpRequest.upload
属性访问。 - 请求中没有使用
ReadableStream
对象
按照上面的规则,因为本次实际请求设置了自定义字段故触发了预检请求。