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

相关推荐
2401_857622664 小时前
SpringBoot框架下校园资料库的构建与优化
spring boot·后端·php
2402_857589364 小时前
“衣依”服装销售平台:Spring Boot框架的设计与实现
java·spring boot·后端
哎呦没6 小时前
大学生就业招聘:Spring Boot系统的架构分析
java·spring boot·后端
_.Switch6 小时前
Python Web 应用中的 API 网关集成与优化
开发语言·前端·后端·python·架构·log4j
杨哥带你写代码7 小时前
足球青训俱乐部管理:Spring Boot技术驱动
java·spring boot·后端
AskHarries8 小时前
读《show your work》的一点感悟
后端
A尘埃8 小时前
SpringBoot的数据访问
java·spring boot·后端
yang-23078 小时前
端口冲突的解决方案以及SpringBoot自动检测可用端口demo
java·spring boot·后端
Marst Code8 小时前
(Django)初步使用
后端·python·django
代码之光_19808 小时前
SpringBoot校园资料分享平台:设计与实现
java·spring boot·后端