本章简要介绍了 Google API 的错误模型。它还为开发者提供了正确生成和处理错误的通用指南。
Google API 使用独立于协议的简单错误模型,这使我们能够在不同的 API、不同的 API 协议(如 gRPC 或 HTTP)和不同的错误上下文(如异步、批处理或工作流错误)中提供一致的体验。
错误模型
Google API 的错误模型由 google.rpc.Status
逻辑定义,该实例在发生 API 错误时返回给客户端。以下代码段显示了错误模型的总体设计:
package google.rpc;
// The `Status` type defines a logical error model that is suitable for
// different programming environments, including REST APIs and RPC APIs.
message Status {
// A simple error code that can be easily handled by the client. The
// actual error code is defined by `google.rpc.Code`.
int32 code = 1;
// A developer-facing human-readable error message in English. It should
// both explain the error and offer an actionable resolution to it.
string message = 2;
// Additional error information that the client code can use to handle
// the error, such as retry info or a help link.
repeated google.protobuf.Any details = 3;
}
由于大多数 Google API 采用面向资源的 API 设计,因此错误处理遵循相同的设计原则,使用一小组标准错误配合大量资源。例如,服务器没有定义不同类型的“找不到”错误,而是使用一个标准 google.rpc.Code.NOT_FOUND
错误代码并告诉客户端找不到哪个特定资源。错误空间变小降低了文档的复杂性,在客户端库中提供了更好的惯用映射,并降低了客户端的逻辑复杂性,同时不限制是否包含可操作信息。
错误代码
Google API 必须使用 google.rpc.Code
定义的规范错误代码。单个 API 应避免定义其他错误代码,因为开发人员不太可能编写用于处理大量错误代码的逻辑。作为参考,每次 API 调用平均处理三个错误代码意味着,大多数应用逻辑只会用于错误处理,这会给开发者带来良好的体验。
错误消息
错误消息应该可以帮助用户轻松快捷地理解和解决 API 错误。通常,在编写错误消息时请考虑以下准则:
- 不要假定用户是您的 API 的专家用户。用户可以是客户端开发者、运维人员、IT 人员或应用的最终用户。
- 不要假设用户了解有关服务实现的任何信息,或者熟悉错误的上下文(例如日志分析)。
- 如果可能,应构建错误消息,以便技术用户(但不一定是 API 开发人员)可以响应错误并改正。
- 确保错误消息内容简洁。如果需要,请提供一个链接,便于有疑问的读者提问、提供反馈或详细了解错误消息中不方便说明的信息。此外,可使用详细信息字段来提供更多信息。
错误详情
Google API 为错误详细信息定义了一组标准错误负载,您可在 google/rpc/error_details.proto 中找到这些错误负载。 它们涵盖了对于 API 错误的最常见需求,例如配额失败和无效参数。与错误代码一样,开发者应尽可能使用这些标准载荷。
只有在可以帮助应用代码处理错误的情况下,才应引入其他错误详细信息类型。如果错误信息只能由人工处理,则应根据错误消息内容,让开发人员手动处理,而不是引入其他错误详细信息类型。
下面是一些示例 error_details
载荷:
ErrorInfo
提供既稳定又可扩展的结构化错误信息。RetryInfo
:描述客户端何时可以重试失败的请求,这些内容可能在以下方法中返回:Code.UNAVAILABLE
或Code.ABORTED
QuotaFailure
:描述配额检查失败的方式,这些内容可能在以下方法中返回:Code.RESOURCE_EXHAUSTED
BadRequest
:描述客户端请求中的违规行为,这些内容可能在以下方法中返回:Code.INVALID_ARGUMENT
错误信息
ErrorInfo
是一种特殊类型的错误载荷。它可提供稳定且可扩展的错误信息,可供人工和应用参考。
每个 ErrorInfo
都包含三条信息:错误网域、错误原因和一组错误元数据,如示例所示。如需了解详情,请参阅 ErrorInfo
定义。
对于 Google API,主要错误网域是 googleapis.com
,相应的错误原因由 google.api.ErrorReason
枚举定义。如需了解详情,请参阅 google.api.ErrorReason
定义。
错误本地化
google.rpc.Status
中的 message
字段面向开发人员,必须使用英语。
如果需要面向用户的错误消息,请使用 google.rpc.LocalizedMessage
作为您的详细信息字段。虽然 google.rpc.LocalizedMessage
中的消息字段可以进行本地化,请确保 google.rpc.Status
中的消息字段使用英语。
默认情况下,API 服务应使用经过身份验证的用户的语言区域设置或 HTTP Accept-Language
标头或请求中的 language_code
参数来确定本地化的语言。
错误映射
可以在不同的编程环境中访问 Google API。每种环境通常都有自己的错误处理方法。以下部分介绍了错误模型在常用环境中的映射方式。
HTTP 映射
虽然 proto3 消息具有原生 JSON 编码,但 Google 的 API 平台对 Google 的 JSON HTTP API 使用了不同的错误架构,以实现向后兼容性。
架构:
// This message defines the error schema for Google's JSON HTTP APIs.
message Error {
// Deprecated. This message is only used by error format v1.
message ErrorProto {}
// This message has the same semantics as `google.rpc.Status`. It uses HTTP
// status code instead of gRPC status code. It has extra fields `status` and
// `errors` for backward compatibility with [Google API Client
// Libraries](https://developers.google.com/api-client-library).
message Status {
// The HTTP status code that corresponds to `google.rpc.Status.code`.
int32 code = 1;
// This corresponds to `google.rpc.Status.message`.
string message = 2;
// Deprecated. This field is only used by error format v1.
repeated ErrorProto errors = 3;
// This is the enum version for `google.rpc.Status.code`.
google.rpc.Code status = 4;
// This corresponds to `google.rpc.Status.details`.
repeated google.protobuf.Any details = 5;
}
// The actual error payload. The nested message structure is for backward
// compatibility with [Google API Client
// Libraries](https://developers.google.com/api-client-library). It also
// makes the error more readable to developers.
Status error = 1;
}
示例(链接):
{
"error": {
"code": 400,
"message": "API key not valid. Please pass a valid API key.",
"status": "INVALID_ARGUMENT",
"details": [
{
"@type": "type.googleapis.com/google.rpc.ErrorInfo",
"reason": "API_KEY_INVALID",
"domain": "googleapis.com",
"metadata": {
"service": "translate.googleapis.com"
}
}
]
}
}
gRPC 映射
不同的 RPC 协议采用不同方式映射错误模型。对于 gRPC,生成的代码和每种支持语言的运行时库为错误模型提供原生支持。您可在 gRPC 的 API 文档中了解更多信息。例如,请参阅 gRPC Java 的 io.grpc.Status
。
客户端库映射
Google 客户端库可能会根据语言选择采用不同方式表达错误,以与既定习语保持一致。例如,google-cloud-go 库将返回一个错误,该错误实现与 google.rpc.Status
相同的接口,而 google-cloud-java 将引发异常。
处理错误
下面的表格包含在 google.rpc.Code
中定义的所有 gRPC 错误代码及其原因的简短描述。要处理错误,您可以检查返回状态代码的说明并相应地修改您的调用。
HTTP | gRPC | 说明 |
---|---|---|
200 | OK |
无错误。 |
400 | INVALID_ARGUMENT |
客户端指定了无效参数。如需了解详情,请查看错误消息和错误详细信息。 |
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 |
出现未知的服务器错误。通常是服务器错误。 |
500 | INTERNAL |
出现内部服务器错误。通常是服务器错误。 |
501 | NOT_IMPLEMENTED |
API 方法未通过服务器实现。 |
502 | 不适用 | 到达服务器前发生网络错误。通常是网络中断或配置错误。 |
503 | UNAVAILABLE |
服务不可用。通常是服务器已关闭。 |
504 | DEADLINE_EXCEEDED |
超出请求时限。仅当调用者设置的时限比方法的默认时限短(即请求的时限不足以让服务器处理请求)并且请求未在时限范围内完成时,才会发生这种情况。 |
重试错误
客户端可能使用指数退避算法重试 503 UNAVAILABLE 错误。 除非另有说明,否则最小延迟应为 1 秒。 除非另有说明,否则默认重试重复应当仅一次。
对于 429 RESOURCE_EXHAUSTED 错误,客户端可能会在更高层级以最少 30 秒的延迟重试。此类重试仅对长时间运行的后台作业有用。
对于所有其他错误,重试请求可能并不适用。首先确保您的请求具有幂等性,并查看 google.rpc.RetryInfo
以获取指导。
传播错误
如果您的 API 服务依赖于其他服务,则不应盲目地将这些服务的错误传播到您的客户端。在翻译错误时,我们建议执行以下操作:
- 隐藏实现详细信息和机密信息。
- 调整负责该错误的一方。例如,从另一个服务接收
INVALID_ARGUMENT
错误的服务器应该将INTERNAL
传播给它自己的调用者。
重现错误
如果您通过分析日志和监控功能无法解决错误,则应该尝试通过简单且可重复的测试来重现错误。您可以使用该测试来收集问题排查的相关信息,您可以在联系技术支持团队时提供这些信息。
我们建议您使用 curl -v
和系统参数来重现 Google API 错误。它们可以共同重现几乎所有 Google API 请求,并为您提供详细的调试信息。如需了解详情,请参阅您要调用的 API 的相应文档页面。
生成错误
如果您是服务器开发者,则应该生成包含足够信息的错误,以帮助客户端开发者理解并解决问题。同时,您必须重视用户数据的安全性和隐私性,避免在错误消息和错误详细信息中披露敏感信息,因为错误通常会被记录下来并且可能被其他人访问。例如,诸如“客户端 IP 地址不在 allowlist 128.0.0.0/8 上”之类的错误消息会披露服务器端政策的相关信息,用户可能无法访问日志。
要生成正确的错误,首先需要熟悉 google.rpc.Code
,然后才能为每个错误条件选择最合适的错误代码。服务器应用可以并行检查多个错误条件,并返回第一个错误条件。
下表列出了每个错误代码和恰当的错误消息示例。
HTTP | gRPC | 错误消息示例 |
---|---|---|
400 | INVALID_ARGUMENT |
请求字段 x.y.z 是 xxx,预期为 [yyy, zzz] 内的一个。 |
400 | FAILED_PRECONDITION |
资源 xxx 是非空目录,因此无法删除。 |
400 | OUT_OF_RANGE |
参数“age”超出范围 [0,125]。 |
401 | UNAUTHENTICATED |
身份验证凭据无效。 |
403 | PERMISSION_DENIED |
使用权限“xxx”处理资源“yyy”被拒绝。 |
404 | NOT_FOUND |
找不到资源“xxx”。 |
409 | ABORTED |
无法锁定资源“xxx”。 |
409 | ALREADY_EXISTS |
资源“xxx”已经存在。 |
429 | RESOURCE_EXHAUSTED |
超出配额限制“xxx”。 |
499 | CANCELLED |
请求被客户端取消。 |
500 | DATA_LOSS |
请参阅备注。 |
500 | UNKNOWN |
请参阅备注。 |
500 | INTERNAL |
请参阅备注。 |
501 | NOT_IMPLEMENTED |
方法“xxx”未实现。 |
503 | UNAVAILABLE |
请参阅备注。 |
504 | DEADLINE_EXCEEDED |
请参阅备注。 |
错误负载
google.rpc
软件包定义了一组标准错误载荷,它们优先于自定义错误载荷。下表列出了每个错误代码及其匹配的标准错误负载(如果适用)。 我们建议高级应用在处理错误时在 google.rpc.Status
中查找这些错误负载。
HTTP | gRPC | 建议的错误详细信息 |
---|---|---|
400 | INVALID_ARGUMENT |
google.rpc.BadRequest |
400 | FAILED_PRECONDITION |
google.rpc.PreconditionFailure |
400 | OUT_OF_RANGE |
google.rpc.BadRequest |
401 | UNAUTHENTICATED |
google.rpc.ErrorInfo |
403 | PERMISSION_DENIED |
google.rpc.ErrorInfo |
404 | NOT_FOUND |
google.rpc.ResourceInfo |
409 | ABORTED |
google.rpc.ErrorInfo |
409 | ALREADY_EXISTS |
google.rpc.ResourceInfo |
429 | RESOURCE_EXHAUSTED |
google.rpc.QuotaFailure |
499 | CANCELLED |
|
500 | DATA_LOSS |
google.rpc.DebugInfo |
500 | UNKNOWN |
google.rpc.DebugInfo |
500 | INTERNAL |
google.rpc.DebugInfo |
501 | NOT_IMPLEMENTED |
|
503 | UNAVAILABLE |
google.rpc.DebugInfo |
504 | DEADLINE_EXCEEDED |
google.rpc.DebugInfo |