实现cors跨域

cors 跨域是前端的一个老生常谈的问题,有很多文章都讲过相关的原理。

如果只看不做,则会停留在"八股文"的层面--记得快,忘得也快;

只有亲自配置过相关的 header,才能对 cors 有更深的理解。

下面,我们就来根据 MDN 的文档来实现 cors 跨域。

技术栈 为了方便开发,前后端使用的技术栈如下: 前端:axios 后端:koa2

简单请求

前端(端口号 8080,下同)

javascript 复制代码
import axios from "axios";

res = await axios.post(
  "http://localhost:3000/simple",
  {},
  {
    headers: {},
  }
);

后端(端口号 3000,下同)

javascript 复制代码
router.get("/simple", async (ctx, next) => {
  ctx.set("Access-Control-Allow-Origin", "http://localhost:8080");
  ctx.body = "simple get response";
});

运行效果

可能出现的问题

如果不满足简单请求的条件,则浏览器会自动发送预检请求

复杂请求

options 请求是预检请求,由浏览器自动发出,请求头自动带上 Access-Control-Request-HeadersAccess-Control-Request-Method, 响应头需要加上 Access-Control-Allow-Origin, Access-Control-Allow-MethodAccess-Control-Allow-Headers

options 请求完成后,浏览器会检查响应头相关字段和正式请求是否对应。

预检请求、正式请求的相关 header 字段对应关系如下

options 请求头 options 响应头 正式请求的请求头 正式请求的响应头
Access-Control-Request-Headers Access-Control-Allow-Headers 字段必须要在 Access-Control-Allow-Headers 的范围内
Access-Control-Expose-Headers定义的字段必须要出现在响应头中
Access-Control-Request-Method Access-Control-Allow-Method Request Method
Access-Control-Allow-Origin Origin Access-Control-Allow-Origin

如果对应,则发出正式请求;

如果对应不上,则出现 cors 错误(无论 http 状态码是不是 20x)

对于正式请求,响应头也需要带上 Access-Control-Allow-Origin,且值和预检请求的响应头一样。

实现代码如下

前端

javascript 复制代码
res = await axios.post(
  "http://localhost:3000/complex",
  {},
  {
    headers: {
      "Content-Type": "application/json",
      "Cache-Control": "no-cache",
    },
  }
);

后端

javascript 复制代码
router.options("/complex", async (ctx, next) => {
  ctx.set("Access-Control-Allow-Origin", "http://localhost:8080");
  ctx.set("Access-Control-Allow-Method", "POST,PUT");
  ctx.set("Access-Control-Allow-Headers", "Content-Type,Cache-Control");
  ctx.set("Access-Control-Max-Age", 86400);
  ctx.status = 204;
});

运行效果

常见的几个错误场景

1. 如果正式请求的请求头和预检请求的响应头对不上,正式请求就会出现 cors 错误,如下图所示

同时,浏览器会出现报错

Access to XMLHttpRequest at 'http://localhost:3000/complex' from origin 'http://localhost:8080' has been blocked by CORS policy: Method DELETE is not allowed by Access-Control-Allow-Methods in preflight response.

2. 如果正式请求的响应头缺少 Access-Control-Allow-Origin 字段,一样会报错

Access to XMLHttpRequest at 'http://localhost:3000/complex/file' from origin 'http://localhost:8080' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

3. 如果预检请求返回失败,正式请求一样会发出,然后出现 cors 错误

响应头有 Content-Coposition,但是 js 代码中读取不到值

下方的代码中, text的值是undefined

javascript 复制代码
const res = await axios.post(
          "http://localhost:3000/complex/file",
          null,
          {
            headers: {
              "Content-Type": "application/octet-stream",
            },
            responseType: "blob",
          }
        );
        const text = decodeURI(res.headers["content-conposition"]);

打印response,结果如下:

原因

默认情况下只暴露安全的响应头,需要用 Access-Control-Expose-Headers 设置允许跨域暴露的响应头

developer.mozilla.org/zh-CN/docs/...

解决

在后端设置响应头

javascript 复制代码
ctx.set("Access-Control-Expose-Headers", "Content-Conposition");

参考资料

developer.mozilla.org/zh-CN/docs/...

www.ruanyifeng.com/blog/2016/0...

相关推荐
怪兽20141 小时前
请例举 Android 中常用布局类型,并简述其用法以及排版效率
android·面试
余道各努力,千里自同风1 小时前
el-input 输入框宽度自适应宽度
javascript·vue.js·elementui
Southern Wind2 小时前
Vue 3 多实例 + 缓存复用:理念及实践
前端·javascript·vue.js·缓存·html
六点半8882 小时前
【计算机网络】初识HTTP(超文本传输协议)
网络协议·计算机网络·http
南一Nanyi2 小时前
才知道 DNS 还能基于 HTTPS 实现!
网络协议·安全·面试
xxxxxxllllllshi2 小时前
Java中Elasticsearch完全指南:从零基础到实战应用
java·开发语言·elasticsearch·面试·职场和发展·jenkins
前端拿破轮2 小时前
从0到1搭一个monorepo项目(一)
前端·javascript·git
_星辰大海乀2 小时前
网络原理 -- HTTP
java·服务器·http·get方法·post方法
Mintopia3 小时前
🎨 AIGC 内容过滤技术:当创作的洪流遇上理性的堤坝
前端·javascript·aigc
绝无仅有3 小时前
面试真实经历某商银行大厂数据库MYSQL问题和答案总结(二)
后端·面试·github