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

相关推荐
qq_297574671 小时前
【实战教程】SpringBoot 集成阿里云短信服务实现验证码发送
spring boot·后端·阿里云
韩立学长3 小时前
【开题答辩实录分享】以《智能大学宿舍管理系统的设计与实现》为例进行选题答辩实录分享
数据库·spring boot·后端
编码者卢布5 小时前
【Azure Storage Account】Azure Table Storage 跨区批量迁移方案
后端·python·flask
她说..8 小时前
策略模式+工厂模式实现审批流(面试问答版)
java·后端·spring·面试·springboot·策略模式·javaee
梦梦代码精9 小时前
开源、免费、可商用:BuildingAI一站式体验报告
开发语言·前端·数据结构·人工智能·后端·开源·知识图谱
李慕婉学姐10 小时前
【开题答辩过程】以《基于Spring Boot的疗养院理疗管理系统的设计与实现》为例,不知道这个选题怎么做的,不知道这个选题怎么开题答辩的可以进来看看
java·spring boot·后端
tb_first10 小时前
SSM速通2
java·javascript·后端
一路向北⁢10 小时前
Spring Boot 3 整合 SSE (Server-Sent Events) 企业级最佳实践(一)
java·spring boot·后端·sse·通信
风象南10 小时前
JFR:Spring Boot 应用的性能诊断利器
java·spring boot·后端
爱吃山竹的大肚肚10 小时前
微服务间通过Feign传输文件,处理MultipartFile类型
java·spring boot·后端·spring cloud·微服务