1
最近完成了一个番茄闹钟webApp:WJW-番茄土豆,小伙伴在帮忙测试bug的时候发现浏览器每次发送正式请求之前,都会发送一个OPTIONS请求:
于是小伙伴本着考察我的目的对我发出了提问:为什么会发送OPTIONS请求?
2
2.1 什么是OPTIONS请求?
CORS MDN是这么描述的
跨域资源共享标准新增了一组 HTTP 首部字段,允许服务器声明哪些源站通过浏览器有权限访问哪些资源。另外,规范要求,对那些可能对服务器数据产生副作用的 HTTP 请求方法(特别是 GET 以外的 HTTP 请求,或者搭配某些 MIME 类型的 POST 请求),浏览器必须首先使用OPTIONS方法发起一个预检请求(preflight request),从而获知服务端是否允许该跨域请求。服务器确认允许之后,才发起实际的 HTTP 请求。在预检请求的返回中,服务器端也可以通知客户端,是否需要携带身份凭证(包括 Cookies 和 HTTP 认证相关数据)。
OPTIONS请求即预检请求,可用于检测服务器允许的http
方法。当发起跨域请求时,由于安全原因,触发一定条件时浏览器会在正式请求之前自动先发起OPTIONS请求,即CORS
预检请求,服务器若接受该跨域请求,浏览器才继续发起正式请求。
2.2 哪些请求会发送options请求?
这里就要说说请求的分类:简单请求和预检请求。
简单请求:满足以下几种情况(日常开发基本上只会注意前两种)
- 使用
GET、POST、HEAD
其中一种方法- 只使用了如下的安全首部字段,不得人为设置其他首部字段
Accept
Accept-Language
Content-Language
Content-Type
仅限以下三种:
text/plain
multipart/form-data
application/x-www-form-urlencoded
HTML
头部header field
字段:DPR、Download、Save-Data、Viewport-Width、WIdth
- 请求中的任意
XMLHttpRequestUpload
对象均没有注册任何事件监听器;XMLHttpRequestUpload
对象可以使用XMLHttpRequest.upload
属性访问- 请求中没有使用
ReadableStream
对象
预检请求:满足以下几种情况
1.使用了
PUT、DELETE、CONNECT、OPTIONS、TRACE、PATCH
方法
2.人为设置了非规定内的其他首部字段,参考上面简单请求的安全字段集合,还要特别注意Content-Type
的类型
XMLHttpRequestUpload
对象注册了任何事件监听器- 请求中使用了
ReadableStream
对象
请求附带身份凭证 >. cookies
发起请求时设置
withCredentials
标志设置为true
,从而向服务器发送cookie
, 但是如果服务器端的响应中未携带Access-Control-Allow-Credentials: true
,浏览器将不会把响应内容返回给请求的发送者。
对于附带身份凭证的请求,服务器不得设置Access-Control-Allow-Origin
的值为*
, 必须是某个具体的域名。
注意,简单的GET
请求不会触发预检,如果对此类带有身份凭证请求的响应中不包Access-Control-Allow-Credentials: true
,这个响应将被忽略掉,并且浏览器也不会将相应内容返回给网页
2.3 OPTIONS请求有什么?
- 预检请求头request header的关键字段:
Access-Control-Request-Method
:告诉服务器实际请求所使用的HTTP
方法
Access-Control-Request-Headers
:告诉服务器实际请求所携带的自定义首部字段
服务器基于从预检请求头部获得的信息来判断,是否接受接下来的实际请求。
- 预检响应头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
此次options请求返回了响应头的内容,但没有返回响应实体response body内容。
2.4 OPTIONS请求是否可以优化?
当然是可以的:
- OPTIONS预检请求的结果可以被缓存
·
Access-Control-Max-Age
这个响应首部表示 preflight request (预检请求)的返回结果(即Access-Control-Allow-Methods
和Access-Control-Allow-Headers
提供的信息) 可以被缓存的最长时间,单位是秒。(MDN)
·如果值为 -1,则表示禁用缓存,每一次请求都需要提供预检请求,即用OPTIONS请求进行检测。
- 避免触发
参考2.2 哪些请求会发送options请求? ,通过设置请求(比如三种Content-Type
)来避免触发预检请求。