了解 API 错误

本指南介绍了 Data Manager API 如何处理和传达错误。 了解 API 错误的结构和含义对于构建 稳健的应用至关重要,这些应用可以妥善处理从输入无效到 服务暂时不可用的问题。

Data Manager API 遵循基于 gRPC 状态代码的标准 Google API 错误模型。每个导致 错误的 API 响应都包含一个 Status 对象,其中包含:

  • 数字错误代码。
  • 错误消息。
  • 可选的其他错误详细信息。

规范错误代码

Data Manager API 使用一组由 gRPC 和 HTTP 定义的规范错误代码。这些代码提供了错误类型的高级指示。 您应始终先检查此代码,以了解问题的基本性质 。

如需详细了解这些代码,请参阅 API 设计指南 - 错误 代码

处理错误

当请求失败时,请按照以下步骤操作:

  1. 检查错误代码以查找错误类型。

    • 如果您使用 gRPC,则错误代码位于 code 字段的 Status 中。 如果您使用客户端库,它可能会抛出与错误代码对应的 特定类型的异常。例如, 如果错误代码为 INVALID_ARGUMENT,则 Java 的客户端库会抛出 com.google.api.gax.rpc.InvalidArgumentException
    • 如果您使用 REST,则错误代码位于 error.status 的错误响应中, 相应的 HTTP 状态位于 error.code 中。
  2. 检查错误代码的标准详细信息载荷。标准详细信息载荷是一组来自 Google API 的错误消息。它们以 结构化且一致的方式为您提供错误详细信息。Data Manager API 中的每个错误都可能包含 多条标准详细信息载荷消息。Data Manager API 客户端库 具有辅助方法,可从错误中获取标准详细信息 载荷。

    无论错误代码是什么,我们都建议您检查并记录 ErrorInfoRequestInfoHelpLocalizedMessage载荷。

    • ErrorInfo 包含其他载荷中可能没有的信息。
    • RequestInfo 包含请求 ID,如果您需要 与支持团队联系,此 ID 会很有用。
    • HelpLocalizedMessage 包含链接和其他详细信息,可帮助您 解决错误。

    此外,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_idaccount_idoperating_account 的第一个项的 destinations 列表中。

description

解释该值导致错误的原因。

reason

ErrorReason 枚举,例如 INVALID_HEX_ENCODINGINVALID_CURRENCY_CODE

BadRequest 示例

以下是包含 BadRequest 消息的 INVALID_ARGUMENT 错误的示例响应。field_violations 表明错误是 accountId 不是数字。fielddestinations[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 列表显示了两个 错误:

  1. 第一个 event 的值不是事件的第二个 用户标识符的十六进制编码。

  2. 第二个 event 的值不是事件的第三个 用户标识符的十六进制编码。

{
  "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 失败的 ErrorInfoErrorInfo 提供了有关错误的更多信息,例如 :

  • 与请求关联的项目,位于 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"
        }
      },
      ...
    ]
  }
}

HelpLocalizedMessage

检查 HelpLocalizedMessage 载荷,以获取指向 文档的链接和本地化错误消息,这些内容可帮助您了解和修复 错误。

例如,以下是因使用未启用 Data Manager API 的 Google Cloud 项目的凭据而导致的 PERMISSION_DENIED 失败的 HelpLocalizedMessageHelp 载荷显示了可启用服务 的网址,而 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_ARGUMENTNOT_FOUNDPERMISSION_DENIEDFAILED_PRECONDITIONUNAUTHENTICATED。这些 错误需要更改请求或应用的状态/凭据。 请勿在不解决问题的情况下重试请求。
  • 服务器错误:代码,例如 UNAVAILABLEINTERNALDEADLINE_EXCEEDEDUNKNOWN。这些错误表明 API 服务存在暂时性问题。
实现重试策略

确定是否可以重试错误,并使用重试策略。

  • 仅针对暂时性服务器错误(例如 UNAVAILABLEDEADLINE_EXCEEDEDINTERNALUNKNOWNABORTED)重试
  • 使用 指数退避算法,在重试之间等待的时间越来越长 。这有助于避免使已经压力过大的 服务不堪重负。例如,等待 1 秒,然后等待 2 秒,再等待 4 秒,以此类推,直到达到 最大重试次数或总等待时间。
  • 向退避延迟添加少量随机“抖动”,以防止 许多客户端同时重试而导致“雷鸣般的羊群”问题。
全面记录日志

记录完整的错误响应,包括所有标准详细信息载荷, 尤其是请求 ID。如果需要,这些信息对于 调试和向 Google 支持团队报告问题至关重要。

提供用户反馈

根据 标准详细信息载荷中的代码和消息,向应用的用户提供清晰且 有用的反馈。例如,您可以说“缺少交易 ID”或“找不到目标账号 ID”,而不是仅仅说“发生 错误”。

遵循这些准则,您可以有效地诊断和处理 Data Manager API 返回的错误 ,从而获得更稳定且用户友好的 应用。