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;
请求头信息
| Key | Value |
|---|---|
| Accept | */* |
| User-Agent | Thunder Client (https://www.thunderclient.com) |
| Authorization | test |
解决方案
问题根源是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
相关产品推荐
相关产品推荐

