You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Express设置错误状态码后仍返回200问题排查

Express权限校验中间件状态码异常问题

我用TypeScript搭配Express编写服务端代码,通过中间件实现权限校验逻辑。当发送携带无效Authorization请求头的请求时,响应内容符合预期,但状态码始终返回200,而非设定的401。已在Thunder Client、Postman及浏览器中测试,结果一致。

环境信息:

  • Node.js v22.2.0
  • 打包工具:Rollup
  • 运行工具:Nodemon

问题代码片段

app.use((req, res, next) => {
    if (!process.env.UNIVERSAL_FS_PASSWORD) {
      throw new Error(
        "An environment variable UNIVERSAL_FS_PASSWORD is required to protect your files"
      );
    }

    if (!req.headers.authorization) {
      return res
        .json({success: false, error: "An Authorization header is required"})
        .status(401);
    }

    if (
      !bycrypt.compareSync(
        process.env.UNIVERSAL_FS_PASSWORD,
        (req.headers.authorization as string).replace(/^Bearer\s/, "")
      )
    ) {
      return res
        .json({success: false, error: "Unauthorized request"})
        .status(401);
    }

    next();
  });

完整精简代码

const initServer = () => {
  const app = express();
  const port = 3000;

  app.use(express.json());

  app.use((req, res, next) => {
    if (!process.env.UNIVERSAL_FS_PASSWORD) {
      throw new Error(
        "An environment variable UNIVERSAL_FS_PASSWORD is required to protect your files"
      );
    }

    if (!req.headers.authorization) {
      return res
        .json({success: false, error: "An Authorization header is required"})
        .status(401);
    }

    if (
      !bycrypt.compareSync(
        process.env.UNIVERSAL_FS_PASSWORD,
        (req.headers.authorization as string).replace(/^Bearer\s/, "")
      )
    ) {
      return res
        .json({success: false, error: "Unauthorized request"})
        .status(401);
    }

    next();
  });

  app.use((req, res, next) => {
    if (!req.query.method || req.query.method === "") {
      return res.json({error: "A method is required"}).status(422);
    }

    if (req.method === "POST" && !req.body) {
      return res
        .json({error: "A body is required on post requests"})
        .status(422);
    }
    next();
  });

  app.get("/:path", async (req, res) => {
    switch (req.query.method) {
      case "readFile":
        let fileOptions: {
          encoding?: null | undefined;
          flag?: string | undefined;
        } | null = null;
        let fileBuffer: Buffer | null = null;

        if (isJson(req.headers.options as string)) {
          fileOptions = JSON.parse(req.headers.options as string);
        }

        try {
          fileBuffer = fs.readFileSync(req.params.path, fileOptions);

          return res.json({success: true, buffer: fileBuffer});
        } catch (err: any) {
          return res.json({success: false, error: err}).status(500);
        }
       // ...
      default:
        // This should never trigger because of the first check
        return res.json({error: "Method not found"}).status(422);
    }
  });
    //...
  });


  app.listen(port, () => {
    console.info(`Listening on port ${port}`);
  });
};

export default initServer;

请求头信息

KeyValue
Accept*/*
User-AgentThunder Client (https://www.thunderclient.com)
Authorizationtest

解决方案

问题根源是Express响应方法的调用顺序错误:res.json()会自动将响应状态码设置为200,并且立即发送响应。此时再调用.status(401)已经无法修改状态码,因为响应已经完成发送。

必须先调用.status()设置状态码,再调用.json()发送响应内容。修改后的核心代码如下:

app.use((req, res, next) => {
    if (!process.env.UNIVERSAL_FS_PASSWORD) {
      throw new Error(
        "An environment variable UNIVERSAL_FS_PASSWORD is required to protect your files"
      );
    }

    if (!req.headers.authorization) {
      // 先设状态码,再返回JSON
      return res.status(401).json({success: false, error: "An Authorization header is required"});
    }

    if (
      !bycrypt.compareSync(
        process.env.UNIVERSAL_FS_PASSWORD,
        (req.headers.authorization as string).replace(/^Bearer\s/, "")
      )
    ) {
      return res.status(401).json({success: false, error: "Unauthorized request"});
    }

    next();
  });

同时,需要将代码中所有类似的错误调用顺序统一修正:

  • 中间件里的422状态码:res.status(422).json(...)
  • 接口异常的500状态码:res.status(500).json(...)

注意事项

所有需要自定义状态码的响应,都必须遵循先设置状态码,再发送响应内容的顺序,否则状态码会被Express默认的200覆盖。


内容的提问来源于stack exchange,提问作者Ethan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 18:05:15