API•
2025-06-05
•7 min
•alltools.one Team
APIRESTJSONResponse FormatBest Practices
API 响应格式:构建一致性 API 的最佳实践
不一致的 API 响应是前端开发者最常抱怨的问题。当每个端点返回的数据结构都不同时,客户端代码中就会充斥着各种特殊处理。一致的响应格式能改善开发者体验、减少 Bug,并使你的 API 具备自文档化能力。
响应信封
将每个响应都包装在一致的结构中:
成功响应(单个资源)
{
"data": {
"id": "user_123",
"name": "Alice",
"email": "alice@example.com",
"createdAt": "2024-01-15T10:30:00Z"
},
"meta": {
"requestId": "req_abc123"
}
}
成功响应(集合)
{
"data": [
{ "id": "user_123", "name": "Alice" },
{ "id": "user_456", "name": "Bob" }
],
"meta": {
"total": 142,
"page": 1,
"perPage": 20,
"requestId": "req_def456"
}
}
错误响应
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Request validation failed",
"details": [
{ "field": "email", "message": "Invalid email format" }
]
},
"meta": {
"requestId": "req_ghi789"
}
}
关键原则:客户端始终检查顶层的 data 或 error。永远不要在同一个响应中混合成功数据和错误信息。
使用我们的 JSON 验证器 验证你的响应格式。
HTTP 状态码
正确使用状态码——它们是客户端首先检查的内容:
成功状态码
| 状态码 | 使用场景 |
|---|---|
| 200 OK | GET 成功,PUT/PATCH 成功 |
| 201 Created | POST 创建资源成功 |
| 204 No Content | DELETE 成功(无响应体) |
客户端错误状态码
| 状态码 | 使用场景 |
|---|---|
| 400 Bad Request | 请求语法格式错误 |
| 401 Unauthorized | 缺少或无效的身份认证 |
| 403 Forbidden | 已认证但未授权 |
| 404 Not Found | 资源不存在 |
| 409 Conflict | 资源状态冲突(重复) |
| 422 Unprocessable | 语法正确但语义错误 |
| 429 Too Many Requests | 超出速率限制 |
服务端错误状态码
| 状态码 | 使用场景 |
|---|---|
| 500 Internal Server Error | 意外的服务器故障 |
| 502 Bad Gateway | 上游服务故障 |
| 503 Service Unavailable | 临时过载或维护中 |
错误响应设计
良好的错误响应能帮助开发者快速调试:
{
"error": {
"code": "RESOURCE_NOT_FOUND",
"message": "User with ID 'user_999' not found",
"details": [],
"documentationUrl": "https://api.example.com/docs/errors#RESOURCE_NOT_FOUND"
},
"meta": {
"requestId": "req_xyz789",
"timestamp": "2024-01-15T10:30:00Z"
}
}
规则:
code是机器可读的(常量字符串,不是 HTTP 状态码)message是人类可读的(可以修改而不影响客户端)details提供验证失败时的字段级错误requestId使支持团队能在日志中追踪请求
分页模式
基于偏移量的分页
简单且支持跳转到任意页面:
{
"data": ["..."],
"meta": {
"page": 2,
"perPage": 20,
"total": 142,
"totalPages": 8
},
"links": {
"first": "/api/users?page=1&per_page=20",
"prev": "/api/users?page=1&per_page=20",
"next": "/api/users?page=3&per_page=20",
"last": "/api/users?page=8&per_page=20"
}
}
基于游标的分页
更适合实时数据和大型数据集:
{
"data": ["..."],
"meta": {
"hasNext": true,
"hasPrev": true
},
"links": {
"next": "/api/users?cursor=eyJpZCI6MTQzfQ&limit=20",
"prev": "/api/users?cursor=eyJpZCI6MTIzfQ&limit=20&direction=prev"
}
}
日期和时间
始终使用带时区信息的 ISO 8601 格式:
{
"createdAt": "2024-01-15T10:30:00Z",
"updatedAt": "2024-01-15T14:22:33+00:00",
"expiresAt": "2024-12-31T23:59:59Z"
}
不要使用:
- Unix 时间戳(模糊不清:是秒还是毫秒?)
- 不带时区的本地日期
- 自定义格式如 MM/DD/YYYY
关于时间戳处理的更多内容,请参阅我们的 Unix 时间戳指南。
Null 与缺失字段
两种常见方法:
包含 null 值(显式):
{ "name": "Alice", "avatar": null, "bio": null }
省略缺失字段(稀疏):
{ "name": "Alice" }
建议:在你的 API 中保持一致。显式 null 值对于强类型语言更好(客户端知道字段存在)。稀疏模式对于带宽受限的环境更好。
响应版本管理
当响应格式发生变化时,请为你的 API 添加版本:
GET /api/v2/users/123
需要版本控制的破坏性变更:
- 删除字段
- 更改字段类型
- 重命名字段
- 更改响应信封结构
无需版本控制的非破坏性变更:
- 添加新字段
- 添加新端点
- 添加新的枚举值
常见问题
我应该将成功响应包装在 data 键中还是直接返回资源?
使用 data 包装器为所有响应提供了一致的结构,并为元数据、分页和链接留出了空间。直接返回资源对于单资源端点更简单。大多数现代 API 使用包装器方法以保持一致性。
如何处理批量操作中的部分失败?
返回 200 状态码,响应中同时包含成功和失败的结果。使用 207 Multi-Status(WebDAV)是另一种选择,但在 REST API 中不太常用。
相关资源
- JSON 格式化工具 — 格式化 API 响应
- JSON API 设计模式 — 全面的 API 设计指南
- JSON Schema 验证 — 验证响应结构