本指南介绍了 Data Manager API 如何处理和传达错误。 了解 API 错误的结构和含义对于构建 稳健的应用至关重要,这些应用可以妥善处理从输入无效到 服务暂时不可用的问题。
Data Manager API 遵循基于
gRPC 状态代码的标准 Google API 错误模型。每个导致
错误的 API 响应都包含一个 Status 对象,其中包含:
- 数字错误代码。
- 错误消息。
- 可选的其他错误详细信息。
规范错误代码
Data Manager API 使用一组由 gRPC 和 HTTP 定义的规范错误代码。这些代码提供了错误类型的高级指示。 您应始终先检查此代码,以了解问题的基本性质 。
如需详细了解这些代码,请参阅 API 设计指南 - 错误 代码。
处理错误
当请求失败时,请按照以下步骤操作:
检查错误代码以查找错误类型。
检查错误代码的标准详细信息载荷。标准详细信息载荷是一组来自 Google API 的错误消息。它们以 结构化且一致的方式为您提供错误详细信息。Data Manager API 中的每个错误都可能包含 多条标准详细信息载荷消息。Data Manager API 客户端库 具有辅助方法,可从错误中获取标准详细信息 载荷。
无论错误代码是什么,我们都建议您检查并记录
ErrorInfo、RequestInfo、Help和LocalizedMessage载荷。ErrorInfo包含其他载荷中可能没有的信息。RequestInfo包含请求 ID,如果您需要 与支持团队联系,此 ID 会很有用。Help和LocalizedMessage包含链接和其他详细信息,可帮助您 解决错误。
此外,
BadRequest载荷对于INVALID_ARGUMENT错误非常有用,因为它提供了有关哪些字段导致错误的信息。
标准详细信息载荷
Data Manager API 最常见的标准详细信息载荷包括:
BadRequest
当请求因
INVALID_ARGUMENT(HTTP 状态代码 400)而失败时,请检查 BadRequest 载荷。
BadRequest 消息表明请求中的字段值不正确,或者缺少必填字段的值。检查
BadRequest 中的 field_violations 列表,找出哪些字段存在错误。每个 field_violations 条目
都包含可帮助您修复错误的信息:
field请求中字段的位置,使用驼峰式路径语法。
如果路径指向列表中的项(
repeated字段),则其索引会 显示在列表名称后的方括号 ([...]) 中。例如,
destinations[0].operating_account.account_id是account_id在operating_account的第一个项的destinations列表中。description解释该值导致错误的原因。
reasonErrorReason枚举,例如INVALID_HEX_ENCODING或INVALID_CURRENCY_CODE。
BadRequest 示例
以下是包含 BadRequest
消息的 INVALID_ARGUMENT 错误的示例响应。field_violations 表明错误是 accountId 不是数字。field 值 destinations[0].login_account.account_id 表明存在字段违规的
accountId 位于 destinations 列表中第一个项
的 login_account 中。
{
"error": {
"code": 400,
"message": "There was a problem with the request.",
"status": "INVALID_ARGUMENT",
"details": [
{
"@type": "type.googleapis.com/google.rpc.ErrorInfo",
"reason": "INVALID_ARGUMENT",
"domain": "datamanager.googleapis.com",
"metadata": {
"requestId": "t-a8896317-069f-4198-afed-182a3872a660"
}
},
{
"@type": "type.googleapis.com/google.rpc.RequestInfo",
"requestId": "t-a8896317-069f-4198-afed-182a3872a660"
},
{
"@type": "type.googleapis.com/google.rpc.BadRequest",
"fieldViolations": [
{
"field": "destinations[0].login_account.account_id",
"description": "String is not a valid number.",
"reason": "INVALID_NUMBER_FORMAT"
}
]
}
]
}
}
以下是包含
BadRequest消息的INVALID_ARGUMENT错误的另一个示例响应。在此示例中,field_violations 列表显示了两个
错误:
{
"error": {
"code": 400,
"message": "There was a problem with the request.",
"status": "INVALID_ARGUMENT",
"details": [
{
"@type": "type.googleapis.com/google.rpc.ErrorInfo",
"reason": "INVALID_ARGUMENT",
"domain": "datamanager.googleapis.com",
"metadata": {
"requestId": "t-6bc8fb83-d648-4942-9c49-2604276638d8"
}
},
{
"@type": "type.googleapis.com/google.rpc.RequestInfo",
"requestId": "t-6bc8fb83-d648-4942-9c49-2604276638d8"
},
{
"@type": "type.googleapis.com/google.rpc.BadRequest",
"fieldViolations": [
{
"field": "events.events[0].user_data.user_identifiers[1]",
"description": "The HEX encoded value is malformed.",
"reason": "INVALID_HEX_ENCODING"
},
{
"field": "events.events[1].user_data.user_identifiers[2]",
"description": "The HEX encoded value is malformed.",
"reason": "INVALID_HEX_ENCODING"
}
]
}
]
}
}
RequestInfo
每当请求失败时,请检查 RequestInfo 载荷。A RequestInfo
包含用于唯一标识您的 API 请求的 request_id。
{
"@type": "type.googleapis.com/google.rpc.RequestInfo",
"requestId": "t-4490c640-dc5d-4c28-91c1-04a1cae0f49f"
}
记录错误或与支持团队联系时,请务必提供 请求 ID,以便帮助诊断问题。
ErrorInfo
检查 ErrorInfo 消息以检索其他标准详细信息载荷中可能未捕获的其他信息。ErrorInfo
载荷包含一个 metadata 映射,其中包含有关错误的信息。
例如,以下是因
使用未启用 Data Manager API 的 Google Cloud 项目的凭据而导致的 PERMISSION_DENIED 失败的 ErrorInfo。ErrorInfo 提供了有关错误的更多信息,例如
:
- 与请求关联的项目,位于
metadata.consumer下。 - 服务名称,位于
metadata.serviceTitle下。 - 可启用服务的网址,位于
metadata.activationUrl下。
{
"error": {
"code": 403,
"message": "Data Manager API has not been used in project PROJECT_NUMBER before or it is disabled. Enable it by visiting https://console.cloud.google.com/apis/api/datamanager.googleapis.com/overview?project=PROJECT_NUMBER then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry.",
"status": "PERMISSION_DENIED",
"details": [
{
"@type": "type.googleapis.com/google.rpc.ErrorInfo",
"reason": "SERVICE_DISABLED",
"domain": "googleapis.com",
"metadata": {
"consumer": "projects/PROJECT_NUMBER",
"service": "datamanager.googleapis.com",
"containerInfo": "PROJECT_NUMBER",
"serviceTitle": "Data Manager API",
"activationUrl": "https://console.cloud.google.com/apis/api/datamanager.googleapis.com/overview?project=PROJECT_NUMBER"
}
},
...
]
}
}
Help 和 LocalizedMessage
检查 Help 和 LocalizedMessage 载荷,以获取指向
文档的链接和本地化错误消息,这些内容可帮助您了解和修复
错误。
例如,以下是因使用未启用 Data Manager API 的 Google Cloud 项目的凭据而导致的 PERMISSION_DENIED
失败的 Help 和 LocalizedMessage。Help 载荷显示了可启用服务
的网址,而 LocalizedMessage 包含错误的说明。
{
"error": {
"code": 403,
"message": "Data Manager API has not been used in project PROJECT_NUMBER before or it is disabled. Enable it by visiting https://console.cloud.google.com/apis/api/datamanager.googleapis.com/overview?project=PROJECT_NUMBER then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry.",
"status": "PERMISSION_DENIED",
"details": [
{
"@type": "type.googleapis.com/google.rpc.LocalizedMessage",
"locale": "en-US",
"message": "Data Manager API has not been used in project PROJECT_NUMBER before or it is disabled. Enable it by visiting https://console.cloud.google.com/apis/api/datamanager.googleapis.com/overview?project=PROJECT_NUMBER then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry."
},
{
"@type": "type.googleapis.com/google.rpc.Help",
"links": [
{
"description": "Google API Console API activation",
"url": "https://console.cloud.google.com/apis/api/datamanager.googleapis.com/overview?project=PROJECT_NUMBER"
}
]
},
...
]
}
}
访问错误详细信息
如果您使用的是某个 客户端库,请使用 辅助方法获取标准详细信息载荷。
.NET
try {
// Send API request
}
catch (Grpc.Core.RpcException rpcException)
{
Console.WriteLine($"Exception encountered: {rpcException.Message}");
var statusDetails =
Google.Api.Gax.Grpc.RpcExceptionExtensions.GetAllStatusDetails(
rpcException
);
foreach (var detail in statusDetails)
{
if (detail is Google.Rpc.BadRequest)
{
Google.Rpc.BadRequest badRequest = (Google.Rpc.BadRequest)detail;
foreach (
BadRequest.Types.FieldViolation? fieldViolation in badRequest.FieldViolations
)
{
// Access attributes such as fieldViolation!.Reason and fieldViolation!.Field
}
}
else if (detail is Google.Rpc.RequestInfo)
{
Google.Rpc.RequestInfo requestInfo = (Google.Rpc.RequestInfo)detail;
string requestId = requestInfo.RequestId;
// Log the requestId...
}
else if (detail is Google.Rpc.ErrorInfo)
{
Google.Rpc.ErrorInfo errorInfo = (Google.Rpc.ErrorInfo)detail;
// Log the errorInfo.Reason and errorInfo.Metadata...
// Log the details in the 'Metadata' map...
foreach (
KeyValuePair<String, String> metadataEntry in errorInfo.Metadata
)
{
// Log the metadataEntry.Key and metadataEntry.Value...
}
}
else
{
// ...
}
}
}
Java
try {
// Send API request
} catch (com.google.api.gax.rpc.InvalidArgumentException invalidArgumentException) {
// Gets the standard BadRequest payload from the exception.
BadRequest badRequest = invalidArgumentException.getErrorDetails().getBadRequest();
for (int i = 0; i < badRequest.getFieldViolationsCount(); i++) {
FieldViolation fieldViolation = badRequest.getFieldViolations(i);
// Access attributes such as fieldViolation.getField() and fieldViolation.getReason()
}
// Gets the standard RequestInfo payload from the exception.
RequestInfo requestInfo = invalidArgumentException.getErrorDetails().getRequestInfo();
if (requestInfo != null) {
String requestId = requestInfo.getRequestId();
// Log the requestId...
}
} catch (com.google.api.gax.rpc.ApiException apiException) {
// Fallback exception handler for other types of ApiException.
// Gets the standard ErrorInfo payload from the exception.
ErrorInfo errorInfo = apiException.getErrorDetails().getErrorInfo();
// Log the 'reason' and 'domain'...
// Log the details in the 'metadata' map...
for (Entry<String, String> metadataEntry : errorInfo.getMetadataMap().entrySet()) {
// Log the metadataEntry key and value...
}
// Gets the standard RequestInfo payload from the exception.
RequestInfo requestInfo = invalidArgumentException.getErrorDetails().getRequestInfo();
if (requestInfo != null) {
String requestId = requestInfo.getRequestId();
// Log the requestId...
}
...
}
错误处理最佳实践
如需构建弹性应用,请遵循以下最佳实践。
- 检查错误详细信息
- 始终查找标准详细信息载荷之一,例如
BadRequest。 每个标准
详细信息载荷都包含可帮助您了解
错误原因的信息。
- 区分客户端错误和服务器错误
确定错误是由实现问题( 客户端)还是 API 问题(服务器)引起的。
- 客户端错误:代码,例如
INVALID_ARGUMENT、NOT_FOUND、PERMISSION_DENIED、FAILED_PRECONDITION、UNAUTHENTICATED。这些 错误需要更改请求或应用的状态/凭据。 请勿在不解决问题的情况下重试请求。 - 服务器错误:代码,例如
UNAVAILABLE、INTERNAL、DEADLINE_EXCEEDED、UNKNOWN。这些错误表明 API 服务存在暂时性问题。
- 客户端错误:代码,例如
- 实现重试策略
确定是否可以重试错误,并使用重试策略。
- 仅针对暂时性服务器错误(例如
UNAVAILABLE、DEADLINE_EXCEEDED、INTERNAL、UNKNOWN和ABORTED)重试 。 - 使用 指数退避算法,在重试之间等待的时间越来越长 。这有助于避免使已经压力过大的 服务不堪重负。例如,等待 1 秒,然后等待 2 秒,再等待 4 秒,以此类推,直到达到 最大重试次数或总等待时间。
- 向退避延迟添加少量随机“抖动”,以防止 许多客户端同时重试而导致“雷鸣般的羊群”问题。
- 仅针对暂时性服务器错误(例如
- 全面记录日志
记录完整的错误响应,包括所有标准详细信息载荷, 尤其是请求 ID。如果需要,这些信息对于 调试和向 Google 支持团队报告问题至关重要。
- 提供用户反馈
根据 标准详细信息载荷中的代码和消息,向应用的用户提供清晰且 有用的反馈。例如,您可以说“缺少交易 ID”或“找不到目标账号 ID”,而不是仅仅说“发生 错误”。
遵循这些准则,您可以有效地诊断和处理 Data Manager API 返回的错误 ,从而获得更稳定且用户友好的 应用。