Google API設計指南-錯誤處理

翻譯自 API Design Guide - Errorsjava

本章簡單介紹 Google API 的錯誤模型以及開發人員如何正確生成和處理錯誤的通常指導。node

Google API 使用了簡單的協議無關的錯誤模型,這容許咱們在不一樣 API,不一樣協議(例如 gRPC 或 HTTP),不一樣的錯誤上下文(如同步、批量處理、工做流錯誤)中提供相同的使用體驗。git

錯誤模型

錯誤模型由 google.rpc.Status 定義。以下所示:github

package google.rpc;

message Status {
  // 容易被客戶端處理的簡單錯誤碼,實際的錯誤碼定義在`google.rpc.Code`
  int32 code = 1;

  // 易於開發者閱讀的錯誤信息,它應該解釋錯誤而且提供可行的解決方法
  string message = 2;

  // 附加的錯誤信息,客戶端可以經過它處理錯誤,例如重試的等待時間或者幫助頁面連接
  repeated google.protobuf.Any details = 3;
}

由於大部分 Google API 使用面向資源的設計,因此錯誤處理經過在大量資源上使用一小組標準錯誤也遵循了相同的設計原則。例如,服務端使用一個標準的 google.rpc.Code.NOT_FOUND 錯誤碼加上特定的資源名來表示「未找到」錯誤,而不是定義不一樣種類的「未找到」錯誤。更少的錯誤狀態減小了文檔的複雜性,在客戶端的庫中提供更好的習慣性映射,在不限制可包含信息的狀況下減小了客戶端邏輯的複雜性。api

錯誤碼

Google API 必須(must) 使用 google.rpc.Code 中定義的標準錯誤碼。單獨的 API 應該(should) 避免定義附加錯誤碼,由於開發者很是不喜歡爲大量錯誤碼編寫處理邏輯。做爲參考,每一個 API 處理平均 3 個錯誤碼意味着大部分程序邏輯在進行錯誤處理,這並非好的開發體驗。安全

錯誤消息

錯誤消息應該幫助用戶輕鬆並快速地 理解並解決 API 錯誤。一般狀況請參考以下規則:併發

  • 不要假設用戶很是瞭解你的 API。用戶多是客戶端開發者、運維人員、IT 人員或者 app 的普通用戶。app

  • 不要假設用戶瞭解服務實現的細節或熟悉錯誤上下文(例如日誌分析)。運維

  • 若是可能的話,應構建錯誤消息,以便技術用戶(但不必定是 API 的開發人員)能夠對錯誤進行響應並更正。ide

  • 保持錯誤信息的簡短。若是須要的話,提供連接以便迷惑的用戶可以提出問題獲得反饋或獲得更多信息。不然,請使用 details 字段來擴展錯誤消息。

錯誤詳情

Google API 爲錯誤詳情定義了一組標準錯誤負載,能夠去 google/rpc/error_details.proto 查看。這裏包含了 API 中最多見的錯誤,例如達到資源限額和錯誤的輸入參數。與錯誤碼相同,錯誤詳情也應該使用標準的負載。

只有在可以幫助程序代碼處理錯誤時才能夠爲錯誤詳情引入新的類型。若是錯誤信息只可以由人(非代碼)處理,應當讓開發者依賴錯誤消息的內容來手動處理,而不是引入新的錯誤詳情類型。若是新的類型被引入,必定要爲它們進行顯式的註冊。

這裏有一些 error_details 負載的示例:

  • RetryInfo 描述了當客戶端可以重試請求時,可能返回 Code.UNAVAILABLECode.ABORTED

  • QuotaFailure 描述了配額檢查失敗的緣由,可能返回 Code.RESOURCE_EXHAUSTED

  • BadRequest 描述了客戶端的非法請求,可能返回 Code.INVALID_ARGUMENT

HTTP 映射

雖然 proto3 有原生的 JSON 編碼,但 Google 的 API 平臺使用以下的 JSON 格式進行錯誤響應,以容許向後兼容:

{
  "error": {
    "code": 401,
    "message": "Request had invalid credentials.",
    "status": "UNAUTHENTICATED",
    "details": [{
      "@type": "type.googleapis.com/google.rpc.RetryInfo",
      ...
    }]
  }
}

| 字段 | 描述 |
| - | - |
| error | 爲了向後兼容 Google API 客戶端庫添加的額外層 |
| code | Status.code 映射爲 HTTP 狀態碼 |
| message | 對應 Status.message |
| status | 對應 Status.code |
| details | 對應 Status.details |

RPC 映射

不一樣的 RPC 協議用不一樣的方法映射到錯誤模型(error model)。對於 gRPC,生成的代碼和全部語言的運行庫都原生支持錯誤模型。你能夠去 gRPC 的 API 文檔中查看詳情。

客戶端庫的映射

Google 客戶端庫可能會選擇按照不一樣的慣例來對不一樣語言進行不一樣的錯誤處理。例如,庫 google-cloud-go 會返回 google.rpc.Status 的實例,而 google-cloud-java 則會拋出異常。

錯誤信息本地化

google.rpc.Status 中的 message 字段是面向開發者的,必須(must) 是英語。

若是須要向用戶提供錯誤信息,請使用 google.rpc.LocalizedMessage做爲詳情字段。google.rpc.LocalizedMessage 能夠被本地化,但請保證 google.rpc.Status 中是英文。

API 服務應該默認使用認證用戶的 locale 或 HTTP Accept-Language 頭來決定本地化語言。

錯誤處理

下表包含了全部在 google.rpc.Code 中定義的 gRPC 錯誤代碼和產生緣由的簡單描述。能夠經過查看返回狀態碼的描述並修改對應的代碼來處理錯誤。

| HTTP | RPC | Description |
| - | - |
| 200 | OK | 沒有錯誤 |
| 400 | INVALID_ARGUMENT | 客戶端使用了錯誤的參數,經過 error message 和 error details 查看更多信息 |
| 400 | FAILED_PRECONDITION | 當前的系統狀態不能執行請求,例如刪除非空目錄 |
| 400 | OUT_OF_RANGE | 客戶端指定無效範圍 |
| 401 | UNAUTHENTICATED | 因爲缺乏、無效或過時的 OAuth 令牌,請求未經過身份驗證 |
| 403 | PERMISSION_DENIED | 客戶端沒有足夠的權限,這多是由於 OAuth 令牌沒有正確的範圍,客戶端沒有權限或者 API 尚未開放 |
| 404 | NOT_FOUND | 指定的資源不存在,或者因爲未公開的緣由(如白名單)請求被拒絕 |
| 409 | ABORTED | 併發衝突,例如讀寫衝突 |
| 409 | ALREADY_EXISTS | 客戶端試圖建立的資源已經存在 |
| 429 | RESOURCE_EXHAUSTED | 超過資源限額或頻率限制,客戶端應該經過 google.rpc.QuotaFailure 查看更多信息 |
| 499 | CANCELLED | 客戶端取消請求 |
| 500 | DATA_LOSS | 不可恢復的數據丟失或損壞,客戶端應該將此錯誤報告給用戶 |
| 500 | UNKNOWN | 服務端未知錯誤,通常是 BUG |
| 500 | INTERNAL | 服務端內部錯誤,通常是 BUG |
| 501 | NOT_IMPLEMENTED | 服務端未實現此 API |
| 503 | UNAVAILABLE | 服務端不可用,通常是服務端掛了 |
| 504 | DEADLINE_EXCEEDED | 請求超過最後期限,若是重複發生,請考慮減小請求的複雜性 |

錯誤重試

當發生 500,503 和 504 錯誤時客戶端 應該(should) 以指數級增加的間隔來重試請求。除非文檔中進行了說明,最小的重試間隔應該是 1 秒。對於 429 錯誤,客戶端應該以最小 30 秒的間隔重試。對於其餘錯誤,重試操做可能並不可行,請先確保請求是冪等的並查看錯誤消息以得到指引。

錯誤傳播

若是 API 服務依賴於其餘服務,則不該盲目地將這些服務中的錯誤傳播給客戶端。翻譯錯誤時,有以下建議:

  • 隱藏實現細節和機密信息

  • 調整負責該錯誤的一方。例如,應把從其餘服務接收到 INVALID ARGUMENT 錯誤轉換爲 INTERNAL 返回給調用者。

生成錯誤

服務端產生的錯誤應該包含足夠多的信息來幫助客戶端開發者理解和解決問題。同時也要當心用戶數據的安全和隱私,由於錯誤常常會被做爲日誌記錄下來並被其餘人查看,因此應避免在錯誤信息和錯誤詳情中暴露敏感信息。例如,錯誤信息「Client IP address is not on whitelist 128.0.0.0/8」將用戶不可訪問的服務端策略暴露出去了。

爲了生成合適的錯誤,你首先應該熟悉 google.rpc.Code 來爲每種錯誤條件選擇最合適的錯誤。服務端程序能夠並行檢查多個錯誤條件,而後返回第一個。

下表列出了每一種錯誤碼和對應的錯誤信息示例。

| HTTP | RPC | 錯誤信息示例 |
| - | - | - |
| 400 | INVALID_ARGUMENT | Request field x.y.z is xxx, expected one of [yyy, zzz]. |
| 400 | FAILED_PRECONDITION | Resource xxx is a non-empty directory, so it cannot be deleted. |
| 400 | OUT_OF_RANGE | Parameter 'age' is out of range [0, 125]. |
| 401 | UNAUTHENTICATED | Invalid authentication credentials. |
| 403 | PERMISSION_DENIED | Permission 'xxx' denied on file 'yyy'. |
| 404 | NOT_FOUND | Resource 'xxx' not found. |
| 409 | ABORTED | Couldn’t acquire lock on resource ‘xxx’. |
| 409 | ALREADY_EXISTS | Resource 'xxx' already exists. |
| 429 | RESOURCE_EXHAUSTED | Quota limit 'xxx' exceeded. |
| 499 | CANCELLED | Request cancelled by the client. |
| 500 | DATA_LOSS | 請看提示 |
| 500 | UNKNOWN | 請看提示 |
| 500 | INTERNAL | 請看提示 |
| 501 | NOT_IMPLEMENTED | Method 'xxx' not implemented. |
| 503 | UNAVAILABLE | 請看提示 |
| 504 | DEADLINE_EXCEEDED | 請看提示 |

提示:由於客戶端不能修復服務端的錯誤,生成額外的錯誤詳情並無用處。爲了不經過 error condition 泄露敏感信息,推薦不要生成任何 error message 而且只生成 google.rpc.DebugInfo 錯誤詳情。DebugInfo 只能用於服務端日誌,不要發送給客戶端。

google.rpc 定義了一組標準錯誤負載,它們優先於自定義的錯誤負載。下表列出了每一個錯誤代碼及其匹配的標準錯誤負載。

| HTTP | RPC | 推薦的錯誤詳情 |
| - | - | - |
| 400 | INVALID_ARGUMENT | google.rpc.BadRequest |
| 400 | FAILED_PRECONDITION | google.rpc.PreconditionFailure |
| 400 | OUT_OF_RANGE | google.rpc.BadRequest |
| 401 | UNAUTHENTICATED | |
| 403 | PERMISSION_DENIED | |
| 404 | NOT_FOUND | |
| 409 | ABORTED | |
| 409 | ALREADY_EXISTS | |
| 429 | RESOURCE_EXHAUSTED | google.rpc.QuotaFailure |
| 499 | CANCELLED | |
| 500 | DATA_LOSS | |
| 500 | UNKNOWN | |
| 500 | INTERNAL | |
| 501 | NOT_IMPLEMENTED | |
| 503 | UNAVAILABLE | |
| 504 | DEADLINE_EXCEEDED |

查看其餘章節

相關文章
相關標籤/搜索