options请求是什么?有什么用?

本文最后更新于:2023年3月19日 晚上

本文转自:https://www.cnblogs.com/mamimi/p/10602722.html

前言

自后台 restful 接口流行开来,请求了两次的情况(options 请求)越来越普遍。笔者也在实际的项目中遇到过这种情况,做一下整理总结。

文章书写思路

为什么发生两次请求

http 的请求方式,包括 OPTIONS、GET、HEAD、POST、PUT、DELETE、TRACE 和 CONNECT 等八种请求方式。其中,get 与 post 只是我们常用的请求方式。
我们能在图一里看到,第一条的请求方式为 options,第二条请求,才是我们预想中的请求。所以为什么发生两条请求的原因就变成了为什么发生 options 请求。

options 请求

options 请求的官方定义:OPTIONS 方法是用于请求获得由 Request-URI 标识的资源在请求/响应的通信过程中可以使用的功能选项。通过这个方法,客户端可以在采取具体资源请求之前,决定对该资源采取何种必要措施,或者了解服务器的性能。
用白话说就是:在发生正式的请求之前,先进行一次预检请求。看服务端返回一些信息,浏览器拿到之后,看后台是否允许进行访问。

如何产生 options 请求

产生 options 请求的原因包括以下几条:
1:**产生了复杂请求**。复杂请求对应的就是简单请求。简单请求的定义是:

  1. 请求方法是 GET、HEAD 或者 POST,并且当请求方法是 POST 时,Content-Type 必须是 application/x-www-form-urlencoded, multipart/form-data 或着 text/plain 中的一个值。
  2. 请求中没有自定义 HTTP 头部。

所谓的自定义头部,在实际的项目里,我们经常会遇到需要在 header 头部加上一些 token 或者其他的用户信息,用来做用户信息的校验。
2:**发生了跨域**。

options 请求有什么作用

官方将头部带自定义信息的请求方式称为带预检(preflighted)的跨域请求。在实际调用接口之前,会首先发出一个 options 请求,检测服务端是否支持真实的请求进行跨域的请求。真实请求在 options 请求中,通过 request-header 将  Access-Control-Request-Headers 与 Access-Control-Request-Method 发送给后台,另外浏览器会自行加上一个 Origin 请求地址。服务端在接收到预检请求后,根据资源权限配置,在 response-header 头部加入 access-control-allow-headers(允许跨域请求的请求头)、access-control-allow-methods(允许跨域请求的请求方式)、access-control-allow-origin(允许跨域请求的域)。另外,服务端还可以通过 Access-Control-Max-Age 来设置一定时间内无须再进行预检请求,直接用之前的预检请求的协商结果即可。浏览器再根据服务端返回的信息,进行决定是否再进行真实的跨域请求。这个过程对于用户来说,也是透明的。
另外在 HTTP 响应头,凡是浏览器请求中携带了身份信息,而响应头中没有返回 Access-Control-Allow-Credentials: true 的,浏览器都会忽略此次响应。
总结:只要是带自定义 header 的跨域请求,在发送真实请求前都会先发送 OPTIONS 请求,浏览器根据 OPTIONS 请求返回的结果来决定是否继续发送真实的请求进行跨域资源访问。所以复杂请求肯定会两次请求服务端。

options 请求如何避免

**其实通过以上的分析,我们能得出以下解决方案:
1:使用代理,避开跨域。
2:将复杂跨域请求更改为简单跨域请求。
3:不使用带自定义配置的 header 头部。

实际案例

笔者现在维护的项目之一是用 angularjs 作为框架,使用$resoruce 进行的通信,请求头默认采用 content-type: application/json。所以即使拿掉请求头里前台自定义的 token,仍然还会在 post 请求中额外发出 options 请求,因为不满足简单请求的条件。而 get 请求没有这这个情况。

笔者将 header 头里的 token 拿掉后,get 的 options 请求消除了。

上图中,post 发出了 options 请求。原因在于 content-type。
去除 post 的 options 请求:

有人想问不想用 options 请求,却又想验证用户信息该怎么做呢?这个时候考虑通过 cookie 进行。