【前端基础-HTTP协议】夸源资源资源共享(CORS)的“预检“”请求

185 阅读4分钟

夸源资源共享是(CORS) 是一种基于HTTP的头机制,该机制可以允许浏览的跨域访问服务器的资源,服务通过设置响应头Access-Control-Allow-Origin来表示除了他自身之外的其他的源( 协议,域名,端口号)。

此外,CORS还有一种机制来检查是服务器是否会允许要发送的真实请求,该机制通过浏览器发起一个到服务器托管的跨源资源的"预检"请求(OPTIONS)。在预检中,浏览器发送的头中标示有 HTTP 方法和真实请求中会用到的头。

图片.png

1. 什么是“预检”请求?

夸源资源共享标准新增了一组HTTP首部字段,允许服务器声明那些源站通过浏览器能够有权限访问那些资源.

规范要求,对于那些有可能对服务器产生的副作用的HTTP请求方法,特别是GET以外的请求,或者那些搭配MIME类型的POST请求,浏览器必须首先使用OPTIONS方法发送一个预检请求,从而获知服务器是否允许真实请求,服务器允许后,才发送实际的请求。

同时在预检请求的返回中,服务器也可以告诉浏览器,是否携带身份认证。

1.为什么要有设置“预检”请求?

简单来讲,避免浏览器带来的副作用。先排除先遣部队躺一波雷,如果能够安全返回,那么大部队就可以放心的出击,如果大部队直接上,可能任务完成了,但是回不来了,这就是一个失败的任务。

出于安全考虑,浏览器发出的网络请求会遵循同源策略,想XMLHttpRequestFetch都会遵守同源策略,一般来讲,浏览器显示跨域请求的方式一般有两种:

  1. 浏览器限制发起的跨域的请求。
  2. 跨域请求可以发起,但是返回的结果会被浏览器拦截。

大多数浏览器都是采用第二种方案,如果没有预检请求的话,就会产生一种情况,对于一些对服务器产生副作用的操作,请求可以正常发出并被服务器处理,可能会服务器中的数据变更,比如修改静态资源或者修改数据库中的数据,但是响应数据却被浏览器拦截了,这就是一次失败的请求。

为了避免这种情况,规范要求对于可能对服务器造成副作用的HTTP请求方法,浏览器必须首先使用OPTIONS方法发送一个预检请求,从而获知服务器是否允许真实请求,服务器允许后,才发送实际的请求。

2.什么情况下会发送“预检”请求?

在跨域资源访问的时候,浏览器会将请求方式分为简单请求和非简单请求,对于非简单请求,浏览器会发送一个“预检请求”,在预检中,浏览器发送的头部,标识有HTTP方法和真实请求会用到的头。

满足下方条件的请求称为简单请求,不会触发options。

  • 请求方式:GET POST HEAD
  • 请求头:
    • Accept   Accept-Language Content-Language
    • Content-Type 告诉服务器我要发送什么类型的数据,仅限于一下三者之一
      • text/plain
      • multipart/form-data
      • application/x-www-form-urlencoded
      • application/json
      • application/xml
    • DPR :客户端设备的像素比
    • DownLink:是 NetworkInformation 接口的一个只读属性,返回以 Mb/s 为单位的有效带宽,并保留该值为 25kb/s 的最接近的整数倍。该值基于最近监测的保持活跃连接的应用层吞吐量,排除了到私有地址空间的连接。当缺少最近的带宽测量数据时,该属性由底层连接技术属性决定。
    • Save-Data:是 NetworkInformation 接口的只读属性, 如果用户设备上设置了减少数据使用的选项时返回 true .
    • Viewport-Width:可视窗口的大小。
    • 请求中没有使用 ReadableStream 对象。
    • 请求中的任意XMLHttpRequestUpload 对象均没有注册任何事件监听器; XMLHttpReq uestUpload 对象可以使用XMLHttpRequest.upload属性访问。

不满足上述条件的跨域都被称为非简单网络请求,都会触发options请求,因为浏览器不确定这些请求会不会对服务器造成不可预知的影响,同时在发出请求的时候,需要服务器返回响应的头部,才可以进行真实的请求。

  • 使用Put、Delete、Trace、Connect请求方法。

    • 服务器需要返回Access-Control-Allow-Methods: POST, GET, OPTIONS
  • 使用了自定义的HTTP头部或不在上面范围的头部,setRequestHeader('X-PINGOTHER', 'pin gpong')

    • 服务器需要返回Access-Control-Allow-Headers: X-PINGOTHER, Content-Type
  • contentType 不符合三者之一。

    • setRequestHeader('Content-Type', 'application/xml')
    • 服务器需要返回Access-Control-Allow-Headers: Content-Type
  • 携带了身份信息的请求,withCredentials = true

    • 服务器需要返回Access-Control-Allow-Credentials: true
  • 优化点:Access-Control-Max-Age 该字段可选,用来指定本次预检请求的有效期,单位为秒,在此期间,不用发出另一条预检请求。