name: api-design description: REST API设计最佳实践,打造一致、直观的API
API设计原则
设计直观、一致且易于使用的API。
URL结构
资源使用名词
# 良好 - 名词
GET /users
GET /users/123
GET /users/123/orders
# 不佳 - 动词
GET /getUsers
GET /fetchUserById/123
POST /createNewUser
复数资源名称
# 良好 - 复数
GET /users
GET /orders
GET /products
# 不佳 - 单数
GET /user
GET /order
层次关系
# 父子关系
GET /users/123/orders # 用户123的订单
GET /orders/456/items # 订单456中的商品
# 避免深层嵌套(最多2层)
# 不佳
GET /users/123/orders/456/items/789/reviews
# 良好 - 需要时扁平化
GET /order-items/789/reviews
HTTP方法
| 方法 | 用途 | 幂等性 | 安全性 |
|---|---|---|---|
| GET | 读取资源 | 是 | 是 |
| POST | 创建资源 | 否 | 否 |
| PUT | 替换资源 | 是 | 否 |
| PATCH | 部分更新 | 是 | 否 |
| DELETE | 删除资源 | 是 | 否 |
GET /users # 列出用户
POST /users # 创建用户
GET /users/123 # 获取用户123
PUT /users/123 # 替换用户123
PATCH /users/123 # 更新用户123
DELETE /users/123 # 删除用户123
请求/响应格式
一致的JSON结构
// 成功响应
{
"data": {
"id": "123",
"name": "张三",
"email": "john@example.com"
}
}
// 集合响应
{
"data": [
{ "id": "123", "name": "张三" },
{ "id": "456", "name": "李四" }
],
"meta": {
"total": 100,
"page": 1,
"perPage": 20
}
}
// 错误响应
{
"error": {
"code": "VALIDATION_ERROR",
"message": "邮箱格式无效",
"details": [
{ "field": "email", "message": "必须是有效的邮箱地址" }
]
}
}
使用camelCase
// 良好
{
"firstName": "张三",
"lastName": "李",
"emailAddress": "john@example.com"
}
// 不佳 - 不一致
{
"first_name": "张三",
"LastName": "李",
"email-address": "john@example.com"
}
状态码
成功 (2xx)
200 OK - GET、PUT、PATCH成功
201 Created - POST成功(包含Location头部)
204 No Content - DELETE成功
客户端错误 (4xx)
400 Bad Request - 语法无效,验证错误
401 Unauthorized - 无/无效身份验证
403 Forbidden - 已认证但无权限
404 Not Found - 资源不存在
409 Conflict - 状态冲突(重复、版本)
422 Unprocessable - 语法有效但语义错误
429 Too Many Requests - 请求频率受限
服务器错误 (5xx)
500 Internal Server Error - 意外错误
502 Bad Gateway - 上游服务失败
503 Service Unavailable - 暂时不可用
504 Gateway Timeout - 上游超时
分页
基于偏移量(简单)
GET /users?page=2&perPage=20
响应:
{
"data": [...],
"meta": {
"total": 100,
"page": 2,
"perPage": 20,
"totalPages": 5
},
"links": {
"first": "/users?page=1&perPage=20",
"prev": "/users?page=1&perPage=20",
"next": "/users?page=3&perPage=20",
"last": "/users?page=5&perPage=20"
}
}
基于游标(可扩展)
GET /users?cursor=eyJpZCI6MTIzfQ&limit=20
响应:
{
"data": [...],
"meta": {
"hasMore": true,
"nextCursor": "eyJpZCI6MTQzfQ"
}
}
过滤与排序
过滤
GET /users?status=active
GET /users?role=admin&status=active
GET /users?createdAt[gte]=2024-01-01
GET /users?name[contains]=john
排序
GET /users?sort=name # 升序
GET /users?sort=-createdAt # 降序(前缀-)
GET /users?sort=lastName,firstName
字段选择
GET /users?fields=id,name,email
GET /users/123?fields=id,name,email
版本控制
URL路径(推荐)
GET /v1/users
GET /v2/users
头部
GET /users
Accept: application/vnd.api+json;version=2
身份验证
Bearer令牌
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
API密钥
X-API-Key: sk_live_abc123
# 或在查询参数中(安全性较低)
GET /users?api_key=sk_live_abc123
频率限制
包含头部:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1640995200
Retry-After: 60
文档
每个端点需要:
- 功能描述
- 请求参数及类型
- 请求体模式
- 各状态码的响应模式
- 请求/响应示例
- 错误情况
# OpenAPI示例
/users:
post:
summary: 创建新用户
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateUser'
responses:
'201':
description: 用户已创建
content:
application/json:
schema:
$ref: '#/components/schemas/User'
'400':
description: 验证错误
常见错误
| 错误 | 问题 | 修复 |
|---|---|---|
| URL中使用动词 | /getUser |
使用 /users/:id |
| 命名不一致 | 大小写混合 | 选择一种(camelCase) |
| 错误的状态码 | 错误时返回200 | 适当使用4xx/5xx |
| 无分页 | 内存问题 | 始终对集合分页 |
| 破坏性变更 | 客户端中断 | 对API进行版本控制 |
| 无频率限制 | 滥用 | 实施限制 |
| 暴露内部信息 | 安全风险 | 转换响应 |
检查清单
- [ ] URL使用名词而非动词
- [ ] HTTP方法使用正确
- [ ] 响应格式一致
- [ ] 适当的状态码
- [ ] 集合分页
- [ ] 支持过滤和排序
- [ ] 实施频率限制
- [ ] 身份验证已记录
- [ ] 错误描述清晰
- [ ] API已版本化