Понимание ошибок API

В этом руководстве объясняется, как API менеджера данных обрабатывает ошибки и передает информацию о них. Понимание структуры и значения ошибок API имеет решающее значение для создания надежных приложений, способных корректно обрабатывать проблемы, от недопустимых входных данных до временной недоступности сервиса.

API Data Manager использует стандартную модель обработки ошибок Google API, основанную на кодах состояния gRPC . Каждый ответ API, приводящий к ошибке, содержит объект Status со следующим содержимым:

  • Числовой код ошибки.
  • Сообщение об ошибке.
  • Дополнительные сведения об ошибке (по желанию).

Канонические коды ошибок

API Data Manager использует набор канонических кодов ошибок, определенных gRPC и HTTP. Эти коды дают общее представление о типе ошибки. Всегда следует сначала проверять этот код, чтобы понять основную суть проблемы.

Более подробную информацию об этих кодах см. в Руководстве по проектированию API — Коды ошибок .

Обработка ошибок

При неудачной попытке запроса выполните следующие действия:

  1. Проверьте код ошибки , чтобы определить тип ошибки.

    • При использовании gRPC код ошибки находится в поле code поля « Status . При использовании клиентской библиотеки может генерироваться исключение определенного типа, соответствующее коду ошибки. Например, клиентская библиотека для Java генерирует исключение com.google.api.gax.rpc.InvalidArgumentException , если код ошибки равен INVALID_ARGUMENT .
    • При использовании REST код ошибки находится в ответе с ошибкой по адресу error.status , а соответствующий HTTP-статус — по адресу error.code .
  2. Проверьте наличие стандартной подробной информации об ошибке. Стандартные подробные сообщения — это набор сообщений об ошибках, поступающих от API Google . Они предоставляют подробную информацию об ошибке в структурированном и согласованном виде. Каждая ошибка от API Data Manager может иметь несколько сообщений стандартной подробной информации. Клиентские библиотеки API Data Manager имеют вспомогательные методы для получения стандартных подробных сообщений об ошибке.

    Независимо от кода ошибки, мы рекомендуем проверить и зарегистрировать данные ErrorInfo , RequestInfo , Help и LocalizedMessage .

    • ErrorInfo содержит информацию, которая может отсутствовать в других полезных нагрузках.
    • RequestInfo содержится идентификатор запроса, что полезно, если вам потребуется связаться со службой поддержки .
    • Help и LocalizedMessage содержат ссылки и другую информацию, которая поможет вам устранить ошибку.

    Кроме того, полезные данные BadRequest , QuotaFailure и RetryInfo пригодятся для обработки конкретных кодов ошибок:

    • Если код состояния INVALID_ARGUMENT , проверьте полезную нагрузку BadRequest , чтобы получить информацию о том, какие поля вызвали ошибку.
    • Если код состояния — RESOURCE_EXHAUSTED , проверьте данные QuotaFailure и RetryInfo , чтобы получить информацию о квотах и ​​рекомендации по задержке повторной попытки.

Стандартные подробные полезные нагрузки

Наиболее распространенные стандартные данные для API Data Manager включают:

BadRequest

При возникновении ошибки INVALID_ARGUMENT (код состояния HTTP 400 ) проверьте наличие полезной нагрузки BadRequest .

Сообщение BadRequest указывает на то, что запрос содержал поля с некорректными значениями или отсутствовало значение в обязательном поле. Проверьте список field_violations в BadRequest , чтобы узнать, какие поля содержат ошибки. Каждая запись field_violations содержит информацию, которая поможет вам исправить ошибку:

field

Местоположение поля в запросе, указанное с использованием синтаксиса пути в стиле Camel Case.

Если путь указывает на элемент списка ( repeated поле), его индекс указывается в квадратных скобках ( [...] ) после имени списка.

Например, destinations[0].operating_account.account_id — это account_id в operating_account первого элемента в списке destinations .

description

Объяснение причины ошибки, вызванной полученным значением.

reason

Перечисление ErrorReason , например INVALID_HEX_ENCODING или INVALID_CURRENCY_CODE .

Примеры BadRequest

Вот пример ответа на ошибку INVALID_ARGUMENT с сообщением BadRequest . Поле field_violations показывает, что ошибка связана с accountId , который не является числом. Значение field destinations[0].login_account.account_id показывает, что accountId с нарушением поля находится в login_account первого элемента в списке destinations .

{
  "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"
          }
        ]
      }
    ]
  }
}

Вот ещё один пример ответа на ошибку INVALID_ARGUMENT с сообщением BadRequest . В этом случае список 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"
          }
        ]
      }
    ]
  }
}

QuotaFailure и RetryInfo

При возникновении ошибки RESOURCE_EXHAUSTED (код состояния HTTP 429 ) необходимо проверить наличие полезных нагрузок QuotaFailure и RetryInfo .

Сообщение QuotaFailure указывает на то, что ресурс исчерпан (например, вы превысили квоту) или система перегружена. Проверьте список violations , чтобы определить, какие квоты были превышены.

Сообщение об ошибке также может содержать информацию RetryInfo , указывающую рекомендуемую retry_delay для повторного выполнения запроса.

RequestInfo

При каждом неудачном запросе проверяйте наличие полезной нагрузки RequestInfo . RequestInfo содержит request_id , который однозначно идентифицирует ваш API-запрос.

{
  "@type": "type.googleapis.com/google.rpc.RequestInfo",
  "requestId": "t-4490c640-dc5d-4c28-91c1-04a1cae0f49f"
}

При регистрации ошибок или обращении в службу поддержки обязательно указывайте идентификатор запроса, чтобы облегчить диагностику проблем.

ErrorInfo

Проверьте наличие сообщения ErrorInfo , чтобы получить дополнительную информацию, которая может не содержаться в других стандартных пакетах подробных данных. Пакет ErrorInfo содержит карту metadata с информацией об ошибке.

Например, вот ErrorInfo об ошибке PERMISSION_DENIED , возникшей из-за использования учетных данных для проекта Google Cloud, в котором API Data Manager не включен. ErrorInfo об ошибке содержит дополнительные сведения, такие как:

  • Проект, связанный с запросом, находится в разделе metadata.consumer .
  • Название сервиса, указанное в metadata.serviceTitle .
  • URL-адрес, по которому можно включить сервис, указан в параметре 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.developers.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.developers.google.com/apis/api/datamanager.googleapis.com/overview?project=PROJECT_NUMBER"
        }
      },
      ...
    ]
  }
}

Help и LocalizedMessage

Проверьте наличие полезных нагрузок Help и LocalizedMessage , чтобы получить ссылки на документацию и локализованные сообщения об ошибках, которые помогут вам понять и исправить ошибку.

Например, вот Help и LocalizedMessage об ошибке PERMISSION_DENIED вызванной использованием учетных данных для проекта Google Cloud, в котором API Data Manager не включен. В Help указан URL-адрес, по которому можно включить службу, а 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.developers.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.developers.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 developers console API activation",
            "url": "https://console.developers.google.com/apis/api/datamanager.googleapis.com/overview?project=PROJECT_NUMBER"
          }
        ]
      },
      ...
    ]
  }
}

Подробности ошибки доступа

Если вы используете одну из клиентских библиотек , воспользуйтесь вспомогательными методами для получения стандартных подробных данных.

.СЕТЬ

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.QuotaFailure)
        {
            Google.Rpc.QuotaFailure quotaFailure = (Google.Rpc.QuotaFailure)detail;
            foreach (
                Google.Rpc.QuotaFailure.Types.Violation violation in quotaFailure.Violations
            )
            {
                // Access attributes such as violation.Subject and violation.QuotaId
            }
        }
        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.QuotaFailureException quotaFailureException) {
  // Gets the standard QuotaFailure payload from the exception.
  QuotaFailure quotaFailure = quotaFailureException.getErrorDetails().getQuotaFailure();
  for (int i = 0; i < quotaFailure.getViolationsCount(); i++) {
    QuotaFailure.Violation violation = quotaFailure.getViolations(i);
    // Access attributes such as violation.getSubject() and violation.getQuotaId()
  }

  // Gets the standard RequestInfo payload from the exception.
  RequestInfo requestInfo = quotaFailureException.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.
  ...
}

Рекомендации по обработке ошибок

Для создания отказоустойчивых приложений следует применять следующие передовые методы.

Проверьте подробности ошибки.
Всегда обращайте внимание на один из стандартных пакетов с подробной информацией, таких как 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 секунды, и так далее до максимального количества повторных попыток или общего времени ожидания.
  • Добавьте небольшое случайное количество «джиттера» к задержкам обратной связи, чтобы предотвратить проблему «грохочущего стада», когда множество клиентов одновременно пытаются повторно отправить запрос.
Тщательно проведите ведение журнала.

Запишите в лог полный ответ об ошибке, включая все стандартные подробные данные, особенно идентификатор запроса . Эта информация необходима для отладки и сообщения о проблемах в службу поддержки Google, если это потребуется.

Предоставьте пользователям обратную связь.

На основе кодов и сообщений в стандартных пакетах данных с подробной информацией предоставьте пользователям вашего приложения четкую и полезную обратную связь. Например, вместо простого «Произошла ошибка» вы можете написать «Отсутствует идентификатор транзакции» или «Идентификатор учетной записи назначения не найден».

Следуя этим рекомендациям, вы сможете эффективно диагностировать и обрабатывать ошибки, возвращаемые API менеджера данных, что приведет к созданию более стабильных и удобных в использовании приложений.