导航


HTML

CSS

JavaScript

浏览器 & 网络

版本管理

框架

构建工具

TypeScript

性能优化

软实力

算法

UI、组件库

Node

冷门技能

介绍

CORS 允许服务器在响应头中指定允许的来源域。前端通过标准的 XMLHttpRequest 或 Fetch 请求与不同域的服务器通信。

而在 CORS 中会有 简单请求 和 复杂请求的概念。

请求类型

简单请求

简单请求不会触发 CORS 预检请求。这样的请求为“简单请求”。

简单请求是一种跨域请求,但由于它满足一组特定的条件,浏览器会直接发送实际的请求,而不需要发送预检请求。这是跨域请求中的一种优化,可以减少不必要的网络请求。

简单请求同时满足以下条件:

  1. 请求方法只能是以下之一:
    1. GET
    2. HEAD
    3. POST
  2. HTTP 头信息只能是以下之一:
  3. 请求中的任何 XMLHttpRequestUpload 对象均没有注册任何事件监听器。
  4. 请求中没有使用 ReadableStream 对象。
  5. 请求中没有使用 FormData 对象。

复杂请求

除以上情况外的。

复杂请求不符合简单请求的条件,通常是因为请求方法是非标准的或者包含自定义头信息。对于复杂请求,浏览器会发送预检请求(OPTIONS 请求)到目标服务器,以获取服务器是否允许跨域请求的信息。

复杂请求的例子

  1. 使用非标准的请求方法,如 PUT、DELETE。
  2. 在请求头中使用自定义的头信息,例如 AuthorizationX-Requested-With
  3. 发送包含文件上传的请求,使用 multipart/form-data
  4. 使用 XMLHttpRequestUploadReadableStream
  5. 使用 FormData 对象上传数据,其中包含文件。

服务端设置

Node 中的解决方案

app.use(async (ctx, next) => {
  ctx.set("Access-Control-Allow-Origin", ctx.headers.origin);
  ctx.set("Access-Control-Allow-Credentials", true);
  ctx.set("Access-Control-Request-Method", "PUT,POST,GET,DELETE,OPTIONS");
  ctx.set(
    "Access-Control-Allow-Headers",
    "Origin, X-Requested-With, Content-Type, Accept, cc"
  );
  if (ctx.method === "OPTIONS") {
    ctx.status = 204;
    return;
  }
  await next();
});

关于 cors 的 cookie 问题

想要传递 cookie 需要满足 3 个条件

1. web 请求设置withCredentials

这里默认情况下在跨域请求,浏览器是不带 cookie 的。但是我们可以通过设置 withCredentials 来进行传递 cookie.

// 原生 xml 的设置方式
var xhr = new XMLHttpRequest();
xhr.withCredentials = true;

// axios 设置方式
axios.defaults.withCredentials = true;

2. Access-Control-Allow-Credentials 为 true

3. Access-Control-Allow-Origin为非 *

这里请求的方式,在 chrome 中是能看到返回值的,但是只要不满足以上其一,浏览器会报错,获取不到返回值。

Untitled

Access to XMLHttpRequest at '<http://127.0.0.1:8080/api/corslist>' from origin '<http://127.0.0.1:8000>' has been blocked by CORS policy: The value of the 'Access-Control-Allow-Credentials' header in the response is '' which must be 'true' when the request's credentials mode is 'include'. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.

Untitled

Access to XMLHttpRequest at '<http://127.0.0.1:8080/api/corslist>' from origin '<http://127.0.0.1:8000>' has been blocked by CORS policy: 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'. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.

Untitled

前端示例

分别演示一下前端部分 简单请求 和 非简单请求****

简单请求

<script src="<https://cdn.bootcss.com/axios/0.19.2/axios.min.js>"></script>
<script>
  axios.get("<http://127.0.0.1:8080/api/corslist>");
</script>

非简单请求

这里我们加入了一个非集合内的 header 头 cc 来达到非简单请求的目的。

<script src="<https://cdn.bootcss.com/axios/0.19.2/axios.min.js>"></script>
<script>
  axios.get("<http://127.0.0.1:8080/api/corslist>", { header: { cc: "xxx" } });
</script>

Untitled

Untitled

提示

在新版的 chrome 中,如果你发送了复杂请求,你却看不到 options 请求。可以在这里设置 chrome://flags/#out-of-blink-cors 设置成 disbale ,重启浏览器。对于非简单请求就能看到 options 请求了。