cors 跨域实测笔记

165 阅读3分钟

测试demo

简单请求

不会触发 CORS 预检请求。这样的请求为 简单请求,。若请求满足所有下述条件,则该请求可视为 简单请求

  1. HTTP 方法限制:只能使用 GET、HEAD、POST 这三种 HTTP 方法之一。如果请求使用了其他 HTTP 方法,就不再被视为简单请求。
  2. 自定义标头限制:请求的 HTTP 标头只能是以下几种常见的标头:AcceptAccept-LanguageContent-LanguageLast-Event-IDContent-Type(仅限于 application/x-www-form-urlencodedmultipart/form-datatext/plain)。HTML 头部 header field 字段:DPR、Download、Save-Data、Viewport-Width、WIdth。如果请求使用了其他标头,同样不再被视为简单请求。
  3. 请求中没有使用 ReadableStream 对象。
  4. 不使用自定义请求标头:请求不能包含用户自定义的标头。
  5. 请求中的任意 XMLHttpRequestUpload 对象均没有注册任何事件监听器;XMLHttpRequestUpload 对象可以使用 XMLHttpRequest.upload 属性访问

服务器端:

const express = require("express");
const app = express();

app.use((req, res, next) => {
  res.header("Access-Control-Allow-Origin", "*");
  res.header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
  res.header("Access-Control-Allow-Headers", "Content-Type, Authorization");
  res.header("Access-Control-Allow-Credentials", true);
  if (req.method === "OPTIONS") {
    res.sendStatus(200);
  } else {
    next();
  }
});

// 当前端发起请求时返回认证信息
app.get("/api/user", (req, res) => {
  //   res.setHeader("Access-Control-Allow-Origin", "http://22.4.48.239:5500");
  res.setHeader("Access-Control-Allow-Origin", "*");
  res.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE");
  //   res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
  //   res.setHeader("Access-Control-Allow-Credentials", true);
  const user = { name: "Alice" };
  res.json(user);
});

// 处理 POST 请求
app.post("/api/data", (req, res) => {
  const data = req.body;
  // 处理数据
  res.json({ message: "Data received!" });
});

app.listen(3000, () => {
  console.log("Server started on port 3000");
});

前端测试页面:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <script src="./axios.js"></script>
  </head>
  <body>
    <script>
      document.cookie = "name=value; expires=Thu, 01 Jan 2099 00:00:00 UTC; ";

// fetch 测试
      //   setTimeout(() => {
      //     fetch("http://22.4.48.239:3000/api/user", { credentials: 'include' })
      //       .then((response) => response.json())
      //       .then((data) => console.log(data))
      //       .catch((error) => console.error(error));
      //   }, 1000);

// axios 测试
      axios
        .get("http://22.4.48.239:3000/api/user", { withCredentials: true })
        .then(function (response) {
          console.log(response);
        });
    </script>
  </body>
</html>

  • 携带认证cookie时, 是携带当前域的cookie, 不是服务接口域的
  • 携带不携带仅从chrome调试器很难看出, 可以从调试器的network面板里, 看服务详情信息的headers面板里的General里有无 Remote Address: xx.xx 信息, 有的就不携带认证. 也可看有无cookie头. 可以从调试器用 copy as fetch可以看出 (这种方式也要看浏览器心情, 测试偶尔可以, 偶尔带不带认证拷出来的fetch请求信息一样的), 见下图:

image.png

拷出来的代码如下:

fetch("http://22.4.48.239:3000/api/user", {
  "headers": {
    "accept": "application/json, text/plain, */*",
    "accept-language": "zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7,de;q=0.6,zh-TW;q=0.5,fr;q=0.4,ko;q=0.3",
    "cache-control": "no-cache",
    "pragma": "no-cache"
  },
  "referrer": "http://22.4.48.239:5500/",
  "referrerPolicy": "strict-origin-when-cross-origin",
  "body": null,
  "method": "GET",
  "mode": "cors",
  "credentials": "include" // include 代表要携带, omit 代表不携带
});

"credentials" 字段值: include 代表要携带, omit 代表不携带

  • 要携带认证, Access-Control-Allow-Origin 必须为请求的域名才可以. 不携带设置为 * 或 请求的域名都可以.
  • get请求可以成功, 但post请求不可以. 要检查是否是对"OPTIONS"请求进行了处理.
//node.js
if(req.method==="OPTIONS"){
    res.sendStatus(200)
}else{
    next()
}

如果有nginx在前面, 要修改配置:

if ($request_method = 'OPTIONS'){
   return 200;
}
  • 类似下面的报错, 说header field 不被允许, 要在 Access-Control-Allow-Headers 设置里添加对应的字段.

Acess to fetch at 'https://xxxx' from origin 'https://yyyyyyyy' has been blocked by CORS policy: Request header field theme is not allowed by Access-Control-Allow-Headers in preflight response.

  • 避免使用含有自定义头部信息的请求:由于浏览器默认不会将某些自定义的头部信息包含在 OPTIONS 请求中,因此如果使用了这些头部信息,则浏览器会自动发送 OPTIONS 请求来预检。因此,如果不需要使用自定义头部信息,则可以避免这种情况发生。

  • 增加 POST 请求的缓存时间:如果已经完成了一次预检,可以设置 POST 请求的缓存时间来避免浏览器重复发送 OPTIONS 请求。在响应头中,可以设置 Access-Control-Max-Age 属性来指定缓存时间(单位为秒)。