Express 中设 HTTP 状态码必须用 res.status(code) 链式调用,再配合 send/json/end 发送响应;顺序错误或遗漏将导致默认 200;res.sendStatus() 仅支持标准状态文本,不支持自定义响应体。

Response 对象怎么设 HTTP 状态码(Node.js Express 场景)
Express 的 res 对象本身不直接暴露「状态码属性」,而是靠 res.status() 链式调用设置,再配合 res.send()、res.json() 或 res.end() 才真正发出响应。漏掉 .status() 或调用顺序错,状态码就还是默认的 200。
-
res.status(404).send('Not found')✅ 正确:先设码,再发体 -
res.send('Not found').status(404)❌ 无效:响应已发送,status()被忽略 -
res.status(404); res.json({ error: 'xxx' })✅ 有效,json()内部会复用已设状态码 - 不调用
res.status()直接res.send()→ 永远是 200,哪怕你 throw 了错误
为什么 res.sendStatus(404) 不能替代 res.status(404).send('xxx')
res.sendStatus() 是快捷方法,只做两件事:设状态码 + 发送对应状态文本(如 404 → 'Not Found'),且不支持自定义响应体。它内部调用了 res.status(code).send(statusMessage),但你无法插入手动 JSON、HTML 或空体。
- 需要返回
{ "error": "user not found" }?必须用res.status(404).json(...) - 只想返回空响应(比如 204 No Content)?
res.sendStatus(204)可以,但更明确写法是res.status(204).end() - 用
res.sendStatus(500)会发字符串 'Internal Server Error',而非你自己的错误结构,前端可能解析失败
fetch / axios 收不到 4xx/5xx 状态码?检查是否被 .catch() 吞掉
浏览器 fetch 默认不会因 HTTP 状态码抛错:404、500 都算“成功响应”,只是 response.status 是非 2xx 值。很多人误以为“没进 catch 就是成功”,结果前端逻辑照常走,UI 却卡在 loading。
-
fetch('/api/user/999').then(r => console.log(r.status))→ 输出 404,但不会进catch - 正确做法:手动检查
if (!response.ok) throw new Error(...) - Axios 默认会把 4xx/5xx 当异常抛出(触发
.catch),但前提是没配validateStatus覆盖规则 - 后端设了 401,但前端没处理
response.status === 401→ 登录态失效却无感知
状态码设早了、设错了,但日志里看不出来?加统一响应拦截
Express 默认不记录实际发出的状态码,尤其在中间件多、异步嵌套深时,res.status() 可能被覆盖或遗漏。一个简单但有效的调试手段是在所有路由末尾加一层响应钩子:
app.use((req, res, next) => {
const originalSend = res.send;
res.send = function(body) {
console.log(`[${req.method} ${req.url}] → ${res.statusCode}`);
return originalSend.call(this, body);
};
next();
});- 这能暴露「明明写了
res.status(400),结果发出去却是 200」的问题(大概率是别的中间件又调了res.status(200)) - 注意:不要在生产环境长期开这个日志,影响性能
- 更稳妥的做法是用
on-finished包监听响应结束事件,避免劫持send带来的兼容风险
状态码不是写完就完的事,它和响应体、客户端处理逻辑、中间件执行顺序绑在一起——少一个环节对不上,前端就收不到你想表达的语义。

















