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 中间件配置, 一了百了。

相关推荐
mldong7 小时前
C# 开发者也有自己的轻量工作流引擎了:NuGet 装包,5 分钟跑通一条审批流
后端·c#·.net
爱学堂IT分享10 小时前
鹤翔老师pmp项目管理培训认证考试课 (直播录播题库送学时证明)【共58课时】-51CTO学堂
后端
王的宝库10 小时前
Go 项目结构:从单文件到标准工程布局
开发语言·后端·golang
码事漫谈11 小时前
类型即世界:一个 C++ 程序员的本体论入门
后端
Rain的Java大神之路11 小时前
JavaWeb开发如何解决跨域问题
java·前端·后端·nginx·web安全·面试·运维开发
IT_陈寒13 小时前
Python的多线程居然是个假把式?搞清GIL让我少熬三天夜
前端·人工智能·后端
明月_清风13 小时前
位图与布隆过滤器:海量数据下的"存在性判断"艺术
前端·后端·算法
明月_清风13 小时前
Hash 表从入门到精通:Go 实战与工程细节
前端·后端·算法
考虑考虑14 小时前
ElasticSearch索引命令
运维·后端·elasticsearch
玉宇夕落15 小时前
背单词项目一:融会贯通:从零构建"单词大师"后台——Next.js + Supabase + Drizzle 全栈开发实战
后端