name: api-design-patterns description: REST API 设计,包括资源命名、分页、版本控制和 OpenAPI 规范生成
API 设计模式
资源命名
- 使用复数名词:
/users、/orders、/products - 嵌套关系:
/users/{id}/orders - 最大嵌套深度:2 层。超过时使用查询参数或顶级资源
- 使用 kebab-case:
/user-profiles,而不是/userProfiles - 不要在 URL 中使用动词:
/users/{id}/activate是错误的,应使用POST /users/{id}/activation
HTTP 方法
| 方法 | 目的 | 幂等性 | 请求体 | 成功状态码 |
|---|---|---|---|---|
| GET | 读取资源 | 是 | 否 | 200 |
| POST | 创建资源 | 否 | 是 | 201 |
| PUT | 完全替换 | 是 | 是 | 200 |
| PATCH | 部分更新 | 否 | 是 | 200 |
| DELETE | 删除资源 | 是 | 否 | 204 |
POST 成功时返回 Location 头,包含创建资源的 URL。
状态码
200 OK - 读取/更新成功
201 Created - 创建成功
204 No Content - 删除成功
400 Bad Request - 验证错误(包括字段级错误)
401 Unauthorized - 缺失或无效认证
403 Forbidden - 已认证但未授权
404 Not Found - 资源不存在
409 Conflict - 状态冲突(重复、版本不匹配)
422 Unprocessable - 语义无效(JSON 有效,值错误)
429 Too Many Reqs - 速率限制(包括 Retry-After 头)
500 Internal Error - 未处理的服务器错误(绝不暴露堆栈跟踪)
错误响应格式
{
"error": {
"code": "VALIDATION_ERROR",
"message": "请求验证失败",
"details": [
{ "field": "email", "message": "必须是有效的邮箱地址" },
{ "field": "age", "message": "必须至少 18 岁" }
]
}
}
在整个 API 中使用一致的错误代码。在 API 参考文档中记录每个代码。
基于游标的分页(首选)
GET /users?limit=20&cursor=eyJpZCI6MTAwfQ
响应:
{
"data": [...],
"pagination": {
"next_cursor": "eyJpZCI6MTIwfQ",
"has_more": true
}
}
对于大型或频繁变化的数据集,使用游标分页。将游标编码为不透明的 base64 字符串。绝不要在游标中暴露原始 ID。
基于偏移量的分页(仅用于简单情况)
GET /users?page=3&per_page=20
响应:
{
"data": [...],
"pagination": {
"page": 3,
"per_page": 20,
"total": 245,
"total_pages": 13
}
}
仅当总计数便宜且数据集较小时使用偏移量分页。
过滤和排序
GET /orders?status=pending&created_after=2025-01-01&sort=-created_at,+total
GET /products?category=electronics&price_min=100&price_max=500
GET /users?search=john&fields=id,name,email
使用字段选择(fields 参数)来减少载荷大小。排序字段前缀 - 表示降序。
版本控制
首选 URL 路径版本控制,因为简单:
/api/v1/users
/api/v2/users
规则:
- 一旦发布 v1,绝不破坏它。添加字段,永不删除。
- 新的必需字段 = 新版本
- 使用
Sunset头和 6 个月通知来弃用旧版本 - 同时最多支持 2 个活动版本
请求/响应头
Content-Type: application/json
Accept: application/json
Authorization: Bearer <token>
X-Request-Id: <uuid> # 用于追踪
X-RateLimit-Limit: 100 # 每个窗口的请求数
X-RateLimit-Remaining: 47 # 窗口中剩余请求数
X-RateLimit-Reset: 1700000000 # 窗口重置 Unix 时间戳
Retry-After: 30 # 速率限制重置前的秒数
始终在响应中返回 X-Request-Id 用于调试。
OpenAPI 规范指南
- 先写规范,再实现(规范驱动开发)
- 对共享模式使用
$ref:$ref: '#/components/schemas/User' - 为每个端点定义
examples - 对多态响应使用
oneOf/anyOf - 从规范生成客户端 SDK,绝不手动编写
- 在中间件中根据规范验证请求
paths:
/users/{id}:
get:
operationId: getUser
parameters:
- name: id
in: path
required: true
schema:
type: string
format: uuid
responses:
'200':
description: 用户找到
content:
application/json:
schema:
$ref: '#/components/schemas/User'
'404':
$ref: '#/components/responses/NotFound'
速率限制策略
- 应用每用户、每端点限制
- 使用滑动窗口算法(而非固定窗口)
- 返回
429并附带Retry-After头 - 免除健康检查和认证端点的速率限制
- 记录受速率限制的请求,用于滥用检测