[ADP-410] 第三方服務錯誤抽象化
概述
當您的 API 依賴第三方服務(雲端服務提供者、外部 API、資料庫)時,您必須 (MUST) 抽象化內部實作細節並避免向 API 使用者暴露它們。本 ADP 定義了如何將第三方故障轉換為適當的 API 回應,以維護安全性和架構不透明性。
指引
錯誤抽象化要求
- API 回應必須不得 (MUST NOT) 揭露第三方服務名稱、廠商或實作細節。
- API 回應必須不得 (MUST NOT) 暴露第三方錯誤代碼或訊息。
- API 回應必須不得 (MUST NOT) 揭露內部服務拓撲或依賴關係。
- 錯誤回應必須 (MUST) 使用 ADP-403 中定義的通用問題類型。
- 錯誤回應必須 (MUST) 符合 RFC 9457 HTTP Problem Details 格式,如 ADP-401 所述。
狀態碼對應
在轉換第三方錯誤時,API 必須 (MUST) 將它們對應至適當的 HTTP 狀態碼:
503 vs 504: 理解差異
503 和 504 的選擇取決於您 API 的架構角色:
504 Gateway Timeout - 使用時機:
- 您的 API 作為閘道或代理轉發請求至第三方服務
- 第三方服務是完成請求的主要上游依賴
- 您的 API 角色主要是路由、聚合或轉換來自第三方的回應
- 範例: API 閘道聚合來自多個微服務的資料
- 範例: BFF (Backend for Frontend) 轉發請求至後端 API
503 Service Unavailable - 使用時機:
- 第三方是支援您服務的眾多內部依賴之一
- 您的 API 提供自己的商業邏輯,而第三方是支援元件
- 失敗代表影響您服務可用性的暫時性狀況
- 範例: 支付服務中支付閘道是眾多依賴之一 (資料庫、快取等)
- 範例: 使用者服務暫時無法發送通知電子郵件
語義準確性
504 明確表示「我作為閘道,上游失敗了」。503 表示「我暫時無法為您服務」。根據轉發/代理是否是您的主要角色,或第三方只是支援依賴來選擇。
錯誤對應表
- 第三方服務逾時 → 503 或 504 (見上文),必須 (MUST) 包含
Retry-After
標頭 - 第三方服務不可用 → 503 Service Unavailable 加上
Retry-After
標頭 - 第三方超過速率限制 → 503 Service Unavailable (不要暴露是第三方限制)
- 後端認證/授權失敗 → 500 Internal Server Error (永遠不要暴露認證細節)
- 無效的後端配置 → 500 Internal Server Error
- 暫時性網路錯誤 → 503 或 504 (取決於您的架構角色)
問題類型設計
API 應該 (SHOULD) 使用不會暴露實作的通用問題類型:
可接受的問題類型:
/problems/service-unavailable
- 通用不可用 (最抽象)/problems/gateway-timeout
- 閘道/代理逾時/problems/storage-unavailable
- 儲存能力受影響 (功能類別)/problems/payment-service-unavailable
- 支付能力受影響/problems/notification-service-unavailable
- 通知能力受影響
禁止的問題類型 (暴露實作):
/problems/s3-bucket-error
- 暴露 AWS S3 廠商/problems/stripe-error
- 暴露 Stripe 廠商/problems/firebase-auth-timeout
- 暴露 Firebase 廠商/problems/sendgrid-unavailable
- 暴露 SendGrid 廠商
功能類別
您可以 (MAY) 在問題類型中使用功能類別 (儲存、支付、通知) 來指示受影響的能力,但您必須不得 (MUST NOT) 暴露特定廠商名稱。這有助於客戶端了解降級的功能,而不會揭露您的基礎設施選擇。
適當抽象化的範例:
- ✅
storage-unavailable
而非 ❌s3-error
- ✅
payment-service-timeout
而非 ❌stripe-timeout
- ✅
authentication-unavailable
而非 ❌auth0-error
- ✅
notification-failed
而非 ❌sendgrid-503
回應標頭
- API 應該 (SHOULD) 為暫時性失敗 (503 狀態) 包含
Retry-After
標頭。 - API 可以 (MAY) 在提供過期快取資料時包含
Cache-Control
標頭。 - API 可以 (MAY) 包含自訂標頭如
X-Cache-Status: STALE
以指示降級模式。
韌性模式
雖然以下是後端實作細節,但 API 應該 (SHOULD) 實作韌性模式以改善使用者體驗:
- 斷路器 (Circuit Breaker): 暫時停止呼叫失敗的服務以防止連鎖故障。當斷路器開啟時,回傳快取資料或降級回應。
- 逾時: 設定適當的逾時以防止長時間等待。達到逾時時回傳 503 Service Unavailable。
- 後備策略: 如果可能,回傳降級功能而不是完全失敗。
INFO
這些模式是內部實作細節。API 使用者應該 (SHOULD) 只透過標準 HTTP 回應看到結果—他們必須不得 (MUST NOT) 看到斷路器狀態或其他內部韌性機制。
範例
範例: 服務不可用 (503)
HTTP/1.1 503 Service Unavailable
Content-Type: application/problem+json
Retry-After: 60
{
"type": "/problems/service-unavailable",
"title": "Service Temporarily Unavailable",
"status": 503,
"detail": "The service is temporarily unable to process your request. Please retry after the specified time."
}
優點:
- 通用問題類型
- 沒有暴露實作細節
- 為客戶端提供可操作的指引
- 包含
Retry-After
標頭
範例: 閘道逾時 (504)
當您的 API 作為閘道/代理且上游服務逾時時:
HTTP/1.1 504 Gateway Timeout
Content-Type: application/problem+json
Retry-After: 30
{
"type": "/problems/gateway-timeout",
"title": "Gateway Timeout",
"status": 504,
"detail": "The server did not receive a timely response from an upstream service. Please try again.",
"instance": "/api/resources/123"
}
優點:
- 使用 504 表示閘道/代理角色
- 抽象化「上游服務」(沒有特定服務名稱)
- 包含
Retry-After
為客戶端提供指引 - 適當的語義 HTTP 狀態碼
範例: 功能類別 (儲存不可用)
當您想指示受影響的能力而不暴露哪個廠商時:
HTTP/1.1 503 Service Unavailable
Content-Type: application/problem+json
Retry-After: 60
{
"type": "/problems/storage-unavailable",
"title": "Storage Service Unavailable",
"status": 503,
"detail": "The storage service is temporarily unavailable. Your request has been saved and will be processed when the service recovers.",
"instance": "/api/documents/upload"
}
優點:
- 指示「儲存」能力受影響 (幫助客戶端了解降級的功能)
- 不會揭露它是 AWS S3、Azure Blob 或任何特定廠商
- 提供可操作的資訊而不暴露基礎設施
- 維持架構抽象化
範例: 內部錯誤
HTTP/1.1 500 Internal Server Error
Content-Type: application/problem+json
{
"type": "/problems/internal-error",
"title": "Internal Server Error",
"status": 500,
"detail": "An unexpected error occurred. Please contact support if the problem persists."
}
日誌記錄和可觀察性
雖然 API 回應必須不得 (MUST NOT) 暴露第三方細節,但後端服務必須 (MUST) 為營運團隊記錄完整資訊:
- 完整的第三方錯誤細節和堆疊追蹤
- 用於關聯的請求 ID
- 第三方服務名稱和端點
- 延遲和逾時資訊
- 斷路器狀態轉換
WARNING
此日誌記錄嚴格用於內部營運,必須永遠不得 (MUST NEVER) 透過 API 暴露,即使在外部使用者可存取的開發或除錯模式中也是如此。
相關 ADPs
- ADP-401: HTTP 問題基礎 - 問題細節格式
- ADP-403: 問題類型設計 - 設計問題類型
- ADP-139: Retry-After - 何時重試
- ADP-201: HTTP Status 504 - Gateway timeout