Express cors 中间件和自定义中间件函数设置 Header 的区别

今天又踩了个大坑,问题是这样的。 我正在使用 GraphQL 的 SANDBOX 环境测试接口,需要 Express 配置跨域。origin 的值是 https://studio.apollographql.com , 本来是一件很简单的事情,但是我又折腾了一个下午。

一开始沙盒环境请求 http://localhost:8080/graphql 报错,

bash 复制代码
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at [http://localhost:8080/graphql](http://localhost:8080/graphql "http://localhost:8080/graphql"). (Reason: CORS request did not succeed). Status code: (null).

我心想这简单,配置一下 Access-Control-Allow-Origin 设置为 * 即可。但是呢,因为后续需要模拟登录状态,保存 Cookie, 需要设置 Access-Control-Allow-Credentialstrue, 那么 Access-Control-Allow-Origin 就不能为 * 通配符了。所以 Access-Control-Allow-Origin 需要设置为具体的值 https://studio.apollographql.com ,形成一个类似于白名单的效果。

最终的设置:

js 复制代码
app.use(function (req, res, next) {
    res.setHeader("Access-Control-Allow-Credentials", "true");
    res.setHeader(
        "Access-Control-Allow-Origin",
        "https://studio.apollographql.com"
    );
    res.setHeader(
        "Access-Control-Allow-Methods",
        "GET,HEAD,PUT,PATCH,POST,DELETE"
    );
    next();
});

但是 GraphQL 的沙盒环境还是一直报错,405 Method Not Allowed , 即使设置了 Access-Control-Allow-Methods: GET,HEAD,PUT,PATCH,POST,DELETE 也不行,预检请求响应字段里面还有一个 ALLOW: "GET, POST", 看了一下 MDN 文档,发现是 405 的响应必须要一个 ALLOW 字段,显示当前 server 支持的请求方法列表。

我的疑惑来了,我明明配置了跨域的请求方法,为什么还会报错

想了好久。

cors 中间件设置跨域的参数:

js 复制代码
app.use(
    cors({
        credentials: true,
        origin: process.env.CLIENT_URL,
    })
);

居然就不报错了,奇怪啊。去翻看了一下 cors 的源代码,不长,就两百多行。

我发现除了一些基础的配置之外, cors 做了一个判断,当请求的方法是 OPTIONS 的时候,直接 res.end() 结束了响应,而其他情况,则调用 next(), 继续往下运行。

到这个时候,我明白了。

OPTIONS 预检请求,是询问 server 是否当前 origin 可以访问,一般来讲,预检请求成功返回,就是代表 origin 可以做请求了(由浏览器控制)。这个时候,server 再定义一些字段来精细化控制后续的请求。

我遇到的情况报错是因为, 针对预检请求,没有做特殊处理,导致后续的 Apollo Server 对这个 OPTIONS 请求报错了,导致预检请求失败。前面所做的配置,也就无效了。

所以说, 自定义预检请求的中间件函数应该这样写:

js 复制代码
app.use(function (req, res, next) {
    res.setHeader("Access-Control-Allow-Credentials", "true");
    res.setHeader(
        "Access-Control-Allow-Origin",
        "https://studio.apollographql.com"
    );
    res.setHeader(
        "Access-Control-Allow-Methods",
        "GET,HEAD,PUT,PATCH,POST,DELETE"
    );
    if (req.method === "OPTIONS") {
        res.send();
    } else {
        next();
    }
});

针对预检请求,设置完参数后,直接返回,不要让后续的中间件再进行处理了。

弄清楚问题后,我直接注释了我写的配置,直接使用用 cors 中间件配置, 一了百了。

相关推荐
追逐时光者6 小时前
一款开源、现代化的 WinForm UI 控件库
后端·.net
花月C7 小时前
个性化推荐:基于用户的协同过滤算法
开发语言·后端·算法·近邻算法
cci8 小时前
还在用conda?,试试uv,提高包的安装速度
后端
cci8 小时前
设备每次插入Linux识别的串口不一样?试试udev!
后端
9ilk8 小时前
【C++】--- C++11
开发语言·c++·笔记·后端
码事漫谈10 小时前
VSCode CMake Tools 功能解析、流程与最佳实践介绍
后端
火云牌神10 小时前
本地大模型编程实战(38)实现一个通用的大模型客户端
人工智能·后端
码事漫谈10 小时前
从C++/MFC到CEF与TypeScript的桌面架构演进
后端
冰块的旅行11 小时前
magic-api使用
后端
用户895356032822011 小时前
Goroutine + Channel 高效在哪?一文吃透 Go 并发底层 G-M-P 调度与实现
后端·go