Appearance
HTTP 状态码详解
本章导读:状态码是服务器对"这次请求发生了什么"的官方判决(RFC 9110 §15 定义了完整注册表)。本章按类别精讲每个状态码的语义、义务性头部、适用场景与常见误用,并给出 CRUD 场景的速查矩阵。
0. 阅读须知
- 状态码是三元组:数字 + 原因短语 + 语义规范(原因短语仅为人读,客户端只看数字)。
- "服务器 SHOULD/MUST/MUST NOT"是 RFC 2119 关键词:MUST=义务,SHOULD=默认应做(有正当理由可不做),MAY=可选。
- 自定义状态码只能使用 600~999?——不推荐任何自定义码:既有客户端/代理可能拒绝未知码;业务细分请用
problem+json的type错误码(见上一章)。
1. 1xx 信息性
| 码 | 名称 | 说明 |
|---|---|---|
| 100 | Continue | 客户端发大 body 前用 Expect: 100-continue 试探,服务器说"可以继续发" |
| 101 | Switching Protocols | 协议切换(WebSocket 握手 Upgrade: websocket 就走这个) |
| 103 | Early Hints | 提前返回 Link 预加载提示(配合 200 使用) |
API 设计中 1xx 多由框架/网关自动处理,了解即可。
2. 2xx 成功
200 OK
请求成功,body 含结果表示。GET/PUT/PATCH/POST 的通用成功码。避免:所有响应都 200——错误、创建、无内容场景都有更精确的码。
201 Created
POST 创建新资源成功(或 PUT upsert 创建)。
- 义务:
Location头指向新资源 URI(SHOULD); - body 可返回新资源表示(方便客户端免去二次 GET),也可只给
Location; - 异步创建(尚在处理)用 202 而不是 201。
202 Accepted
请求已受理但处理未完成——异步作业入口:
http
POST /v1/videos
HTTP/1.1 202 Accepted
Location: /v1/jobs/88 ← 用作业资源跟踪进度
Content-Location: /v1/videos/9
Link: </v1/jobs/88>; rel="monitor"204 No Content
成功,无 body(响应必须无 body)。典型:DELETE 成功、PUT/PATCH 成功且不需要返回新表示、OPTIONS 预检。
- 204 ≠ 失败:"没内容"是成功语义;
- 客户端收到 204 应保留当前缓存表示之外的认知:资源已变更(缓存要失效,见缓存章)。
206 Partial Content
范围请求成功(Range: bytes=0-1023 → Content-Range: bytes 0-1023/8665),音视频拖动、断点续传的机制核心。若 Range 不合法返回 416 Range Not Satisfiable。
其它:203 Non-Authoritative Information(代理改写表示)、205 Reset Content(表单场景命令客户端重置输入)——API 罕见。
3. 3xx 重定向与条件
| 码 | 名称 | 方法是否可变 | 使用要点 |
|---|---|---|---|
| 301 | Moved Permanently | POST→GET 可能 | 永久迁移,客户端应改存新 URI;带 Location |
| 302 | Found | 同上 | 临时跳转;不改变客户端已存 URI |
| 303 | See Other | 强制变 GET | POST/PUT 成功后跳到结果页(PRG 模式:Post/Redirect/Get) |
| 304 | Not Modified | — | 条件请求命中,缓存生效;无 body |
| 307 | Temporary Redirect | 不变 | 临时跳转且保持方法/body |
| 308 | Permanent Redirect | 不变 | 永久迁移且保持方法(REST 资源改名优先 308) |
3xx 设计守则:
- 能路由兼容就不要重定向(多一次往返 + 安全策略复杂化);重定向目标尽量同域。
304 Not Modified是缓存系统自动协商的产物(If-None-Match/If-Modified-Since命中),API 代码通常不手动返回。- 301/302 的历史包袱(方法可能被改成 GET)是 307/308 存在的唯一理由——API 迁移请用 308。
4. 4xx 客户端错误
认证授权三连
400 Bad Request 请求本身有问题(语法/参数),改了再发
401 Unauthorized 身份没证明 → 必须带 WWW-Authenticate(RFC 7235)
403 Forbidden 身份知道了,但禁止操作;重试无意义(除非改角色)
405 Method Not Allowed 方法不对 → 必须带 Allow 头
406 Not Acceptable Accept 协商失败
404 vs 403:敏感资源对无权限者伪装 404(防枚举),团队定策略状态与并发的码
| 码 | 场景 | 关键义务/说明 |
|---|---|---|
| 409 Conflict | 与当前资源状态冲突:唯一约束、状态机非法迁移、版本冲突(无 ETag 体系时) | body 说明冲突原因;与 412 区分:412=预条件头失败,409=业务规则冲突 |
| 410 Gone | 资源永久删除/端点已下线 | 比 404 多一层"别再来"语义;SEO 也靠它 |
| 411 Length Required | 需要 Content-Length | 罕见(chunked 上传策略) |
| 412 Precondition Failed | If-Match/If-Unmodified-Since 不满足 | 乐观锁的标准失败响应 |
| 413 Content Too Large | body 超限 | 可带 Retry-After;上传 API 应告知上限 |
| 414 URI Too Long | 请求行超限 | 长查询串改 POST 提交 |
| 415 Unsupported Media Type | Content-Type 不对 | 例:发 text/plain 但只收 JSON |
| 416 Range Not Satisfiable | Range 越界 | 带 Content-Range: */实际大小 |
| 422 Unprocessable Content | 语法正确语义失败 | 校验错误的头号归处(RFC 9110 更名自 Unprocessable Entity) |
| 425 Too Early | 请求可能重放(TLS 0-RTT) | 了解 |
| 428 Precondition Required | 要求带条件头但没带(RFC 6585) | 强制乐观锁的接口用它 |
| 429 Too Many Requests | 限流(RFC 6585) | 必须带 Retry-After;建议 RateLimit 三头 |
4xx 的缓存与监控注意
4xx/5xx 响应默认不可缓存(除非显式 Cache-Control);监控告警统计应以 4xx 比例为客户端健康度指标,5xx 比例为服务健康度指标。
5. 5xx 服务器错误
| 码 | 场景 | 要点 |
|---|---|---|
| 500 Internal Server Error | 未分类异常 | 兜底码;频繁出现 = 缺陷清单没做好映射 |
| 501 Not Implemented | 方法不被支持(比 405 更"诚实") | 一般还是用 405;501 指"整个方法未实现" |
| 502 Bad Gateway | 网关/代理的上游返回无效响应 | 微服务链路常见,排查看上游 |
| 503 Service Unavailable | 过载/维护,暂时性 | 应带 Retry-After;LB 摘除节点后返回它 |
| 504 Gateway Timeout | 上游超时 | 与 502 区分:超时 vs 无效响应 |
| 505 HTTP Version Not Supported | 版本不支持 | 罕见 |
5xx 响应绝不携带堆栈/SQL/内部配置;
Server、X-Powered-By等头部做剥离处理(详见安全章)。
6. CRUD 场景速查矩阵
| 操作 | 成功(有 body) | 成功(无 body) | 失败常见码 |
|---|---|---|---|
| GET 单个 | 200 | — | 404、401、403、410(曾存在) |
| GET 集合 | 200(空列表也是 200) | — | 400(非法过滤参数)、416(范围) |
| POST 创建 | 201(+Location) | — | 400、401、403、409(重复)、422(校验)、429 |
| PUT 替换 | 200 或 204 | 204 | 400、404、401、403、409、412(版本不符)、422 |
| PATCH 修改 | 200 或 204 | 204 | 同 PUT;415(补丁格式错) |
| DELETE 删除 | 200(返回被删资源) | 204 | 404、401、403、409(有依赖)、410(已永久删) |
| 异步创建 | 202(+Location→作业) | — | 401、429 |
| 条件 GET 缓存命中 | — | 304 | — |
7. 本章小结
- 状态码选择三问:成功了该给哪个 2xx?客户端能修吗(4xx)还是服务器能修(5xx)?有没有 MUST 级头部义务(401/405/409/412/429)?
- 重定向家族:308 保方法、303 转 GET、304 给缓存——别再用 302 走天下。
- 422/409/412 是"高级感"三码:会用它们表达校验、冲突、并发,是 API 设计成熟的标志。
8. 下一步
进阶篇开始。先解决"协商"——多格式/多语言/多版本如何选 → 内容协商