外观
HTTP 状态码使用指南
约 1360 字
HTTP 状态码使用指南
状态码是接口的「第一语言」。返回对了,客户端(浏览器、App、 你的同事)都知道该怎么处理;返回错了,监控失灵、缓存混乱、 前端一堆特判代码。这篇按场景讲「该返回什么」。
一、五个分类,记住家族
1xx 信息性 (少见,协议层用,101 升级 WebSocket)
2xx 成功 「你要的,办了」
3xx 重定向 「东西在别处」
4xx 客户端错 「你请求有问题」
5xx 服务端错 「我的问题」写业务接口,2xx/4xx/5xx 是主场。
二、2xx:成功不只有 200
| 码 | 语义 | 场景 |
|---|---|---|
| 200 | OK | GET 成功、PUT/PATCH 成功(返回更新后的资源) |
| 201 | Created | POST 创建成功(响应带 Location 头指向新资源) |
| 202 | Accepted | 已接受,异步处理中(导出任务、批量作业) |
| 204 | No Content | 成功但没内容返回(DELETE 成功) |
两个常见误用:
- POST 创建返回 200:该用 201。创建语义明确, 客户端才知道「新资源产生了」(配合 Location 头);
- DELETE 返回 200 + 一大坨 JSON:该用 204。 删都删了,返回那个被删对象的尸体干嘛。
POST /api/users → 201 Created
Location: /api/users/123
{ "id": 123, "name": "张三" }
DELETE /api/users/123 → 204 No Content三、3xx:重定向与缓存验证
| 码 | 语义 | 场景 |
|---|---|---|
| 301 | 永久重定向 | 域名更换、HTTP→HTTPS。浏览器会缓存 |
| 302 | 临时重定向 | 登录后跳转、临维护页。不缓存 |
| 304 | 未修改 | 协商缓存命中(详见 HTTP 缓存) |
301 和 302 的区别 SEO 敏感:301 把权重传给新地址, 302 不传。域名迁移用 301,别用 302。
四、4xx:请求的锅,一个个说清
400 vs 422
都是「请求有问题」,分工:
- 400:请求格式错 —— JSON 解析失败、缺必填字段、 参数类型不对;
- 422:格式对但语义错 —— 用户名已存在、 余额不足、日期不合理。
(这对界限有争议,团队内统一就行。我倾向:语法问题 400, 业务规则问题 422。)
401 vs 403
最容易混的一对:
- 401 Unauthorized:「我不认识你」—— 没登录、 token 过期/无效。该去登录;
- 403 Forbidden:「认识你,但你没权限」—— 登录了, 但普通用户访问管理员接口。登录也没用。
前端处理逻辑完全不同:401 → 跳登录页;403 → 展示「无权访问」。
404 vs 405
- 404:资源不存在,或路径根本错;
- 405 Method Not Allowed:路径对,方法错 —— 对
/users/123发了 PATCH 但接口只支持 GET/PUT。
405 的响应必须带 Allow 头说明支持哪些方法 (Allow: GET, PUT),这是 RFC 要求。
其他常见
| 码 | 场景 |
|---|---|
| 408 | 请求超时(服务端等客户端发完等急了) |
| 409 Conflict | 唯一键冲突、并发修改冲突 |
| 410 Gone | 资源永久删除了(比 404 语义强,告知别再来) |
| 413 | 上传文件太大 |
| 414 | URL 太长 |
| 429 | 限流(响应带 Retry-After 头) |
五、5xx:服务端的锅
| 码 | 场景 |
|---|---|
| 500 | 兜底:代码抛异常了、依赖炸了 |
| 502 | 网关错:Nginx 找不到后端/后端挂了 |
| 503 | 服务不可用:过载保护、熔断、维护中 |
| 504 | 网关超时:后端太慢,Nginx 等不及了 |
502/503/504 的区别运维天天用:
- 502 = 后面没人(连接失败);
- 504 = 后面有人但太慢(超时切断);
- 503 = 主动拒绝(限流/降级/维护)。
六、最常见的三种错误用法
1. 什么都返 200,错误塞 body
// 反例:HTTP 200,但 code 字段是 5001
{ "code": 5001, "message": "用户不存在", "data": null }后果:监控看不到错误率(HTTP 层全成功)、缓存把错误响应也缓存了、 CDN 不会重试、前端得逐个字段判。状态码表达 HTTP 语义, body 表达业务细节,别混。
2. 500 当万能膏药
参数不校验、业务规则不通过,统统 500。客户端无法区分 「重试可能好」和「重试也没用」。4xx 是请求问题(别重试), 5xx 是服务端问题(可重试),这个区分对自动重试机制是生命线。
3. 403 和 401 乱返
前端拿到 403 不知道该跳登录还是提示没权限, 只好「两个都处理」—— 代码就是这么烂起来的。
七、错误响应体:给个统一格式
{
"error": {
"code": "VALIDATION_FAILED",
"message": "邮箱格式不正确",
"details": [
{ "field": "email", "message": "缺少 @" }
]
}
}code:程序判断用,稳定的字符串(别用数字, 容易和 HTTP 状态码混);message:给人看的,可以变;details:字段级错误,表单回填用。
八、选码决策树
请求处理成功?
├─ 是 → 有内容返回? → 200
│ ├─ 创建了新资源? → 201
│ ├─ 异步受理? → 202
│ └─ 没内容(如删除)? → 204
└─ 否 → 谁的锅?
├─ 客户端的锅 → 请求格式错? → 400
│ 业务规则错? → 422
│ 没登录/token 失效? → 401
│ 没权限? → 403
│ 资源不存在? → 404
│ 方法不支持? → 405
│ 冲突/限流? → 409 / 429
└─ 服务端的锅 → 代码炸了? → 500
网关层问题? → 502/504
主动不可用? → 503小结
状态码口诀:成功 2xx(创建 201 删除 204)、重定向 3xx (301 永久 304 缓存)、客户端错 4xx(400 格式 401 没登录 403 没权限 404 不存在)、服务端错 5xx(500 代码 502 网关 504 超时)。设计接口时对着决策树过一遍,监控、 缓存、重试、前端逻辑全都顺。