关于fetch的跨域问题调研

Pt.1 fetch基本跨域配置

javascript
fetch(resource);
fetch(resource, options);

fetch方法接受两个参数,resources与options

  • resources 获取资源的URL,可以是一个字符串、一个URL对象、或者一个Request对象

  • options 对fetch的自定义设置

在options参数中,可对headers、method、cache、keepalive等属性进行配置,一般来说,前端实现跨域,只需要将此参数的mode属性设为'cors'即可,当然,这也需要后端对Access-Control-Allow-Origin相关的一系列相应标头进行配置,但那并不是前端的范畴

Pt.2 当你配置了mode='cors'却依然无法跨域

1. 普普通通的报错

CORS - Access-Control-Allow-Headers.jpg

CORS - Access-Control-Allow-Origin.jpg

Request header field content-type is not allowed by Access-Control-Allow-Headers in preflight response.

No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

关键内容:

  • Request header field content-type is not allowed by Access-Control-Allow-Headers in preflight response.

  • No 'Access-Control-Allow-Origin' header is present on the requested resource

以上两种报错相对常见,前者为后端未配置好Access-Control-Allow-Headers,后者为未配置好Access-Control-Allow-Origin

需要注意的是,Access-Control-Allow-Origin的报错中会让你set the request's mode to 'no-cors',即将 options.mode 选项设为 no-cors,不要这样做,这不会解决问题

2. 稍稍有点内容的报错

CORS - credentials.jpg

关键内容:The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'.

翻译:当请求的凭证模式为“include”时,响应中“Access-Control-Allow-Origin”标头的值不能是通配符“*”

错误原因:options.credentials——携带资源凭证,表示用户代理是否应该在跨域请求的情况下从其他域发送 cookies,它的取值范围如下:

  • omit: 从不发送 cookies
  • same-origin: 只有当 URL 与响应脚本同源才发送 cookies、HTTP Basic authentication 等验证信息.(浏览器默认值,在旧版本浏览器,例如 safari 11 依旧是 omit,safari 12 已更改)
  • include: 不论是不是跨域的请求,总是发送请求资源域在本地的 cookies、HTTP Basic authentication 等验证信息

mdn web docs 在《使用 Fetch》一文中有注明,当请求使用credentials: 'include'时,响应的 Access-Control-Allow-Origin 不能使用通配符 "*",Access-Control-Allow-Origin 必须是当前请求的源

这种情况往往会在试图用fetch携带cookie时出现

fetch请求在默认情况下是不携带cookie的,无论同源还是跨域,当有携带cookie的需求时,都要配置credentials属性,对于前后端分离开发,往往需要将其配置为'include'以实现跨源携带cookie,在此情况下,后端一般需要设置以下响应标头:

  • Access-Control-Allow-Credentials:是否允许 HTTP 跨源请求携带凭据,设置为true

  • Access-Control-Allow-Origin:指定该响应的资源是否被允许与给定的来源(origin)共享,设置为与请求的标头的origin一致即可

  • Access-Control-Allow-Headers:指示在实际请求中可以使用哪些 HTTP 标头,比如content-typeauthorization

  • Access-Control-Allow-Methods:指定在响应预检请求时访问资源所允许的一个或多个方法

以 Express 为例:

javascript
const server = express();
const cors = require("cors");

server.use(
  cors({
    origin: ["http://192.168.x.xx:xxxx"], // 前端服务地址
    methods: ["DELETE", "PUT", "POST", "GET", "OPTIONS"],
    allowedHeaders: ["content-type"],
    credentials: true,
  })
);

如此即可在使用credentials: 'include'时实现跨域资源共享

参考文档

Fetch API - Web API | MDN