API•
2025-06-05
•7 min
•alltools.one Team
APIRESTJSONResponse FormatBest Practices
API 回應格式:打造一致性 API 的最佳實踐
不一致的 API 回應是前端開發者最常抱怨的問題之一。當每個端點回傳的資料結構都不同時,用戶端程式碼就會充斥各種特殊處理邏輯。一致的回應格式能提升開發者體驗、減少錯誤,並讓你的 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 狀態碼,並在 data 物件中同時包含成功和失敗的結果。使用 207 Multi-Status(WebDAV)是另一種選擇,但在 REST API 中較不常用。
相關資源
- JSON 格式化工具 — 格式化 API 回應
- JSON API 設計模式 — 完整的 API 設計指南
- JSON Schema 驗證 — 驗證回應結構