CORS跨域时,为什么会出现一次动做,两次请求?

提出问题

在开发先后端分离项目时候,咱们总会面临一个跨域问题。前端

众所周知,在之前,跨域能够采用代理、JSONP等方式,而在现代浏览器面前,咱们有了更好的选择,CORSajax

咱们能够经过服务器端设置Access-Control-Allow-Origin响应头,便可使指定来源像访问同源接口同样访问跨域接口。后端

在使用CORS的时候,后台采用token检验机制,前台发送请求必须将token放到Request Header中,那么就须要传输自定义Header信息,这时候细心的你必定会发现一个问题,在前端ajax请求数据的时候,有时候会向后台一次性发送两次请求,这两次请求第一次无返回数据,第二次才会返回正确数据。像下图这个样子,莫名多出了一个 OPTIONS 的请求:跨域

不用怀疑,这不是你的代码有bug,也不是在请求函数中重复调用了请求,由于很明显,两次的 Request Method 是不同的。浏览器

若是你也曾因这个问题,困惑过,迷茫过,不知所因。那么关于这个问题,我将为你给出答案!服务器

对于CORS跨域,有两种不一样的请求类型。cookie

  • 简单跨域请求
  • 复杂跨域请求(带预检的跨域请求)。

简单跨域请求

简单跨域请求是指知足如下两个条件的请求。 一、HTTP方法是如下三种方法之一:app

  • HEAD
  • GET
  • POST

二、HTTP的头信息不超出如下几种字段:前后端分离

  • Accept
  • Accept-Language
  • Content-Language
  • Last-Event-ID
  • Content-Type:只限于三个值,application/x-www-form-urlencoded、multipart/form-data、text/plain

简单跨域请求的部分响应头以下:函数

  • Access-Control-Allow-Origin(必含)- 不可省略,不然请求按失败处理。该项控制数据的可见范围,若是但愿数据对任何人均可见,能够填写"*"。

  • Access-Control-Allow-Credentials(可选) – 该项标志着请求当中是否包含cookies信息,只有一个可选值:true(必为小写)。若是不包含cookies,请略去该项,而不是填写false。这一项与XmlHttpRequest2对象当中的withCredentials属性应保持一致,即withCredentials为true时该项也为true;withCredentials为false时,省略该项不写。反之则致使请求失败。

  • Access-Control-Expose-Headers(可选) – 该项肯定XmlHttpRequest2对象当中getResponseHeader()方法所能得到的额外信息。一般状况下,getResponseHeader()方法只能得到以下的信息:

    • Cache-Control
    • Content-Language
    • Content-Type
    • Expires
    • Last-Modified
    • Pragma

    当你须要访问额外的信息时,就须要在这一项当中填写并以逗号进行分隔。

复杂跨域请求

任何一个不知足简单跨域请求要求的请求,即被认为是复杂请求,也称做带预检的跨域请求。

一个复杂请求不止发送一个包含通讯内容的请求,其中最早发送的是一种**"预检"请求**,此时做为服务端,也须要返回**"预回应"**做为响应。"预检"请求其实是对服务端的一种权限请求,只有当"预检"请求成功返回,实际请求才开始执行。

预请求以OPTIONS形式发送,当中一样包含域,而且还包含了两项CORS特有的内容:

  • Access-Control-Request-Method – 该项内容是实际请求的种类,能够是GET、POST之类的简单请求,也能够是PUT、DELETE等等。
  • Access-Control-Request-Headers – 该项是一个以逗号分隔的列表,当中是复杂请求所使用的头部。

显而易见,这个"预检"请求实际上就是在为以后的实际请求发送一个权限请求,在预回应返回的内容当中,服务端应当对这两项进行回复,以让浏览器肯定请求是否可以成功完成。一旦预回应如期而至,所请求的权限也都已知足,才会发出真实请求,携带真实数据。

解决方法

如今问题所在已经很明显了,那么面对这种跨域预检机制形成的屡次请求问题,咱们能够在后台设置Access-Control-Max-Age来控制浏览器在多长时间内(单位s)无需在请求时发送预检请求,从而减小没必要要的预检请求。

关于CORS的更细致的问题能够查看MDN

相关文章
相关标签/搜索