推荐阅读:
HTTP工作原理、请求方法、请求/响应步骤、Request/Response示例
什么是OPTIONS请求
用于获取目的资源所支持的通信选项。客户端可以对特定的 URL 使用 OPTIONS 方法,也可以对整站(通过将 URL 设置为“*”)使用该方法;
简单来说,就是可以用 OPTIONS 请求去嗅探某个请求在对应的服务器中都支持哪种请求方法;
OPTIONS请求特点
OPTIONS请求是用于请求服务器对于某些接口等资源的支持情况的,包括各种请求方法、头部的支持情况,仅作查询使用。
一个示例
通过curl来发送一个http请求,在响应头中可以发现服务器上这个接口对请求方法以及一些header的使用允许情况,也就是上面说的获取服务器对于某些资源的选项、支持情况。
扩展阅读:Linux curl命令详解
OPTIONS触发
浏览器自主发起的行为称之为:“浏览器行为”。
在某些情况下,普通的 GET 或者 POST 请求会自动触发一次 OPTIONS 请求,当 OPTIONS 请求成功返回后,真正的 AJAX 请求才会再次发起。
前端一般不会主动发起这个请求,但是通过F12 debug页面,一般可以看到同一次请求中会有两个请求方法,其中一次的 Request Method 是 OPTIONS;
提示:上图是做AJAX的POST请求时,使用自定义请求头时触发 OPTIONS 预检请求方法;
某些情况都是什么情况
1)跨域请求,非跨域请求不会出现options请求;
扩展阅读:HTTP访问控制(CORS)、同源策略、跨域和预检请求等问题
2)自定义请求头,如:
3)请求头中的 Content-Type 出现了以下三种之外的格式;
Content-Type: application/x-www-form-urlencoded 表单提交
Content-Type: multipart/form-data 文件上传
Content-Type: text/plain 文本
如:
扩展阅读:HTTP Content-Type
当满足条件12或者13的时候,AJAX 请求就会出现 OPTIONS 请求,OPTIONS请求的意义在于先得到服务器端的确认,才能继续下一步的操作,这也是为什么 OPTIONS 请求也被叫做“预检”请求的原因。
即:当浏览器发起"复杂请求"时会主动发起 OPTIONS预检请求。
跨域共享标准规范要求,对那些可能对服务器数据产生副作用的 HTTP 请求方法,浏览器必须首先使用 OPTIONS 方法发起一个预检请求(preflight request),从而获知服务端是否允许该跨域请求。可以理解为:服务器确认允许之后,才发起实际的 HTTP 请求。
注意:很多人以为同源策略是浏览器不让请求发出去、或者后端拒绝返回数据。实际情况是,请求能正常发出,后端接口正常响应,只是数据到了浏览器后被丢弃了(或被拦截了)。
预检请求可能会失败报错
意思是说:从A到B的AJAX请求已被 CORS策略阻止:出现了不允许(预定义之外)的请求头字段;
CORS 机制,使用额外的 HTTP 头来告诉浏览器让运行在一个 Origin (domain) 上的 Web 应用被准许访问来自不同源服务器上的指定资源。这意味着正确设置 CORS 头信息,使用这些API的Web应用就可以跨域了。
简单请求/复杂请求
某些请求不会触发 CORS 预检请求,这样的请求一般称为"简单请求",而会触发预检的请求则称为"复杂请求"。
简单请求
1)请求方法为GET、HEAD、POST时发的请求;
2)人为设置了规范集合之内的头部字段(预定义),如:
Accept
Accept-Language
Content-Language
Content-Type(三个限制值)
DPR
Downlink
Save-Data
Viewport-Width
Width
3)Content-Type 的值仅限于下列三者之一;
Content-Type: application/x-www-form-urlencoded
Content-Type: multipart/form-data
Content-Type: text/plain
4)请求中的任意 XMLHttpRequestUpload 对象均没有注册任何事件监听器;
5)请求中没有使用 ReadableStream 对象。
复杂请求
1)使用了下面任一 HTTP 方法;
PUT、DELETE、CONNECT、OPTIONS、TRACE、PATCH
2)人为设置了规范集合(预定义)之外的头部字段,即简单请求外的字段;
任何自定义头部字段都会是复杂请求
3)Content-Type 的值设置了以下三种之外的时;
Content-Type: application/x-www-form-urlencoded
Content-Type: multipart/form-data
Content-Type: text/plain
如果设置 Content-Type:application/json;charset=utf-8
时,也是复杂请求,必然触发预检。
OPTIONS关键的头信息
Request header 的关键字段
Access-Control-Request-Method
告知服务器,实际请求将使用 POST 方法
Access-Control-Request-Headers
告知服务器,实际请求将携带的自定义请求首部字段
Response header 的关键字段
Access-Control-Allow-Methods
表明服务器允许客户端使用什么方法发起请求
Access-Control-Allow-Origin
允许跨域请求的域名,如果要允许所有域名则设置为 *
Access-Control-Request-Headers
将实际请求所携带的首部字段告诉服务器
Access-Control-Max-Age
指定了预检请求的结果能够被缓存多久,单位秒(如,10分钟是600)
OPTIONS请求优化
当我们发起跨域请求时,如果是简单请求,那么只会发出一次请求,如果是复杂请求则会先发出 OPTIONS 请求,用于确认目标资源是否支持跨域,然后浏览器会根据服务端响应的 header 自动处理剩余的请求,如果响应支持跨域,则继续发出正常请求,如果不支持,则在控制台显示错误。
由此可见,当触发预检时,跨域请求便会发送 2 次请求,既增加了请求数,也延迟了请求真正发起的时间,影响性能,可以优化 OPTIONS 请求。
1、转为简单请求,如用 JSONP 做跨域请求;
2、对 OPTIONS 请求进行缓存;
服务器端设置 Access-Control-Max-Age
字段,当第一次请求该 URL 时会发出 OPTIONS 请求,浏览器会根据返回的 Access-Control-Max-Age
字段缓存该请求的 OPTIONS 预检请求的响应结果(具体缓存时间还取决于浏览器的支持的默认最大值,一般为 10 分钟)。
在缓存有效期内,该资源的请求(URL 和 header 字段都相同的情况下)不会再触发预检。(chrome 打开控制台可以看到,当服务器响应 Access-Control-Max-Age 时只有第一次请求会有预检,后面不会了。注意要开启缓存,去掉 disable cache 勾选。)
OPTIONS请求总结
OPTIONS 请求就是预检请求,可用于检测服务器允许的 HTTP 方法。当发起跨域请求时,由于安全原因,触发一定条件时浏览器会在正式请求之前自动先发起 OPTIONS 请求,即 CORS 预检请求,服务器若接受该跨域请求,浏览器才继续发起正式请求。