前言
HTTP的请求方式,包括OPTIONS
、GET
、HEAD
、POST
、PUT
、DELETE
、TRACE
和CONNECT
等八种请求方式。
其中,GET
与POST
只是我们常用的请求方式。
但是有时一个接口却发生了两次请求:
- 第一条的请求方式为OPTIONS
- 第二条请求,才是我们预想中的请求
那么为什么发生OPTIONS请求呢?
OPTIONS请求
OPTIONS请求的官方定义:
OPTIONS方法是用于请求获得由Request-URI标识的资源在请求/响应的通信过程中可以使用的功能选项。
通过这个方法,客户端可以在采取具体资源请求之前,决定对该资源采取何种必要措施,或者了解服务器的性能。
用白话说就是:
在发生正式的请求之前,先进行一次预检请求。看服务端返回一些信息,浏览器拿到之后,看后台是否允许进行访问。
OPTIONS请求的原因
产生OPTIONS请求的原因包括以下几条:
1:产生了复杂请求。
复杂请求对应的就是简单请求。
简单请求的定义是:
- 请求方法是GET、HEAD或者POST,
并且当请求方法是POST时,
Content-Type
必须是application/x-www-form-urlencoded
,multipart/form-data
或着text/plain
中的一个值。 - 请求中没有自定义HTTP头部。 所谓的自定义头部,在实际的项目里,我们经常会遇到需要在header头部加上一些token或者其他的用户信息,用来做用户信息的校验。
2:发生了跨域。
OPTIONS请求有什么作用
官方将头部带自定义信息的请求方式称为带预检(preflighted)的跨域请求。
在实际调用接口之前,会首先发出一个OPTIONS请求,检测服务端是否支持真实的请求进行跨域的请求。
在OPTIONS请求中,通过request-header将 Access-Control-Request-Headers
与Access-Control-Request-Method
发送给后台,另外浏览器会自行加上一个Origin
请求地址。
服务端在接收到预检请求后,根据资源权限配置,在response-header
头部加入
access-control-allow-origin
(允许跨域请求的域)access-control-allow-methods
(允许跨域请求的请求方式)access-control-allow-headers
(允许跨域请求的请求头)
另外,服务端还可以通过Access-Control-Max-Age
来设置一定时间内无须再进行预检请求,直接用之前的预检请求的协商结果即可。
浏览器再根据服务端返回的信息,进行决定是否再进行真实的请求。
这个过程我们可以通过代理抓包软件或者浏览器的调试的网络中查看。
另外在HTTP响应头,凡是浏览器请求中携带了身份信息,而响应头中没有返回Access-Control-Allow-Credentials: true
的,浏览器都会忽略此次响应。
OPTIONS请求如何避免
其实通过以上的分析,我们能得出以下解决方案:
- 使用代理,避开跨域。
- 将复杂请求更改为简单请求。
- 不使用自定义Header。
如果上面的中有一条我们没法避免就只能设置认证的生效时间了Access-Control-Max-Age
。
Nginx反代解决跨域
Nginx中在响应中添加如下Header
代码语言:javascript复制location / {
add_header 'Access-Control-Allow-Origin' $http_origin always;
add_header 'Access-Control-Allow-Methods' 'GET,OPTIONS,POST' always;
add_header 'Access-Control-Allow-Headers' '*';
add_header 'Access-Control-Allow-Credentials' 'true' always;
add_header 'Access-Control-Max-Age' 36000;
if ($request_method = OPTIONS ) { return 200; }
}
其中:
Access-Control-Max-Age
,用来指定本次预检请求的有效期,单位为秒。
上面配置中,有效期是10小时(36000秒),在此期间,不用发出另一条预检请求。
Access-Control-Allow-Origin
只能设置为三种情况:
- 星号
*
- 单域名
none
同时还有一个限制就是设置为星号的时候,Access-Control-Allow-Credentials
不能设置为true
。
而Access-Control-Allow-Credentials
则一般是服务器用来设置是否允许前端携带Cookies的标志位,withCredentials
是前端用来表示是否给服务器发请求的时候带上Cookies的标志位。