后端 Web 协议(02):REST API 设计——资源建模、幂等性、错误码、版本管理
更新时间:2026-09-01。本文是
backend/web-protocol/Web 协议第 02 篇,接 HTTP 协议。REST 是 Web API 最广泛使用的设计风格。好的 API 设计让接口直观、一致、易于维护。
本文要回答的问题
- REST 的资源建模怎么设计?URL 应该怎么命名?
- 幂等性是什么?为什么 PUT 幂等而 POST 不幂等?
- 统一的错误码结构怎么设计?
- 版本管理怎么做?URL 版本 vs 请求头版本?
一、资源建模
http
# 资源命名:复数名词,一层一层
# 用户资源
GET /users # 获取用户列表
POST /users # 创建用户
GET /users/{id} # 获取单个用户
PUT /users/{id} # 替换整个用户
PATCH /users/{id} # 部分更新用户
DELETE /users/{id} # 删除用户
# 子资源
GET /users/{id}/orders # 获取用户订单
GET /users/{id}/orders/{id} # 获取用户特定订单
# 操作动词
# 操作要放在资源里,不要用动词路径
# ❌ POST /api/createUser
# ❌ POST /api/deleteUser
# ✅ POST /users
# ✅ DELETE /users/{id}URL 命名规则:
- 使用复数名词:
/users而不是/user - 层级用 / 表示:
/users/{id}/orders - 下划线用连字符代替:
/order-items而不是/order_items - 不要用动词:
/upload→POST /files或POST /files/upload - 参数用 query string:
/users?status=active&page=1
二、幂等性
http
# 幂等:多次执行和一次执行结果一样
# GET:幂等
GET /users/1 # 多次请求,结果一样
# PUT:幂等
PUT /users/1 { "name": "Alice" } # 多次执行,用户1 始终是 Alice
# DELETE:幂等
DELETE /users/1 # 第一次删除成功,后续返回 404 或 204
# POST:不幂等
POST /orders { "product": "book" } # 每次创建新订单,id 不同
# PATCH:不幂等(但可以设计为幂等)
PATCH /users/1 { "status": "active" } # 如果 `status` 是绝对赋值,幂等
PATCH /users/1 { "increment": 1 } # 不幂等,每次加 1幂等性的意义: 网络超时后,客户端可以安全重试幂等请求,不会造成重复操作。
三、统一错误码结构
json
// 统一响应格式
{
"code": "USER_NOT_FOUND",
"message": "用户 123 不存在",
"details": {
"userId": 123
},
"requestId": "req-abc-123"
}
// 成功响应
{
"data": { "id": 1, "name": "Alice" },
"requestId": "req-abc-124"
}
// 分页列表
{
"data": [
{ "id": 1, "name": "Alice" }
],
"pagination": {
"page": 1,
"pageSize": 20,
"total": 100,
"totalPages": 5
}
}常见错误码设计:
| 错误码 | HTTP 状态码 | 说明 |
|---|---|---|
| VALIDATION_ERROR | 400 | 参数校验错误 |
| RESOURCE_NOT_FOUND | 404 | 资源不存在 |
| CONFLICT | 409 | 资源冲突(如重复创建) |
| RATE_LIMITED | 429 | 请求过多 |
| INTERNAL_ERROR | 500 | 内部错误 |
四、版本管理
http
# 方案 1:URL 路径版本(最常用)
GET /v1/users/1
GET /v2/users/1
# 方案 2:请求头版本
GET /users/1
Accept: application/vnd.myapp.v1+json
# 方案 3:参数版本
GET /users/1?version=1推荐: URL 路径版本,最简单直观,浏览器缓存友好。
版本策略:
- 大版本(v1 → v2):不兼容改动,如请求/响应格式变化
- 小版本:兼容改动,如新增字段,用同一个版本
五、分页
http
# 基于偏移量分页
GET /users?page=1&pageSize=20
# 基于游标分页(适合大数据量,性能好)
GET /users?cursor=abc123&limit=20
# 响应
{
"data": [...],
"pagination": {
"page": 1,
"pageSize": 20,
"total": 100,
"nextCursor": "def456",
"hasMore": true
}
}游标分页 vs 偏移量分页:
- 偏移量分页:
LIMIT 20 OFFSET 0,大偏移量时性能差 - 游标分页:
WHERE id > cursor LIMIT 20,性能好,但不容易跳到任意页
六、常见坑对照
| 坑 | 现象 | 对策 |
|---|---|---|
| 动词路径 | POST /createUser,风格不一致 | 用资源名 + 方法 |
| 大小写不一致 | 有的用 userId,有的用 user_id | 统一用 camelCase 或 snake_case |
| 缺少错误码 | 只知道 500,不知道具体错误 | 定义统一错误码结构 |
| 不考虑幂等 | 重试导致重复下单 | 幂等请求用幂等键(Idempotency-Key) |
相关与延伸
下一篇:gRPC 与 Protobuf——服务定义、流式通信、性能对比;HTTP 协议基础,见 HTTP 协议。
一句话总结
REST API 设计:资源用复数名词 + 层级路径,操作不用动词(GET/POST/PUT/DELETE 就够了);GET/PUT/DELETE 幂等可安全重试,POST 不幂等需用幂等键;统一错误码结构(code + message + details),分页用游标避免大偏移量性能问题;版本管理推荐 URL 路径版本 /v1/;响应格式统一 {data, pagination},错误时 {code, message, details, requestId}。