Skip to content
ADP
API Design PrincipleBETA

[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
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
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
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
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

參考資料