本指南說明 Data Manager API 如何處理及傳達錯誤。 瞭解 API 錯誤的結構和意義,對於建構穩健的應用程式至關重要,因為這類應用程式可妥善處理各種問題,從無效輸入到暫時無法使用服務,都能應付自如。
Data Manager API 遵循標準 Google API 錯誤模型,該模型以 gRPC 狀態碼為基礎。如果 API 回應導致錯誤,就會包含 Status 物件,其中含有:
- 數字錯誤代碼。
- 錯誤訊息。
- 選用,其他錯誤詳細資料。
標準錯誤代碼
Data Manager API 會使用 gRPC 和 HTTP 定義的一組標準錯誤代碼。這些代碼會指出錯誤類型。您應一律先檢查這個代碼,瞭解問題的根本性質。
如要進一步瞭解這些代碼,請參閱「API 設計指南 - 錯誤代碼」。
快速失敗模型
Data Manager API 採用快速失敗模型。如果要求含有結構錯誤,或任何記錄的必要欄位驗證失敗,整個要求就會失敗,API 也不會處理該要求中的任何資料。
與部分失敗模型比較
快速失敗模式與其他 Google API (例如 Google Ads API 和 Campaign Manager 360 API) 的部分失敗模式不同。在部分失敗模型中,即使部分記錄有錯誤,要求仍會成功,且回應會包含失敗記錄的錯誤詳細資料。
雖然部分失敗可能很方便,但這會帶來重大風險,因為部分失敗模型不會主動提醒您發生錯誤,您必須明確檢查每個回覆中的錯誤。即使 API 拒絕要求中的許多或所有記錄,要求仍會成功,因此可能會掩蓋重要問題。如果要求中的大量記錄發生錯誤,但您未檢查回應,可能完全不會察覺資料有廣泛問題,只會在幾天或幾週後,累積結果與預期不符時才發現。
快速失敗模型會立即提醒您資料或整合的問題,讓您採取適當行動,避免這些缺點。
使用「validateOnly」檢查快速失敗錯誤
大多數擷取和移除要求都支援 validateOnly 欄位。設定 validateOnly 為 true 時,Data Manager API 會執行與一般要求相同的基本驗證檢查,但不會擷取或移除任何資料。
- 如果要求有錯誤,就會失敗,並傳回與一般要求相同的錯誤回應。
- 如果要求通過驗證,就會成功。回應會包含選填欄位的任何
fieldWarnings,就像一般要求一樣。
validateOnly 可用來:
- 測試新的或更新的整合項目,且不會影響實際資料。
- 請先確認修正內容是否能解決錯誤,再重新傳送要求。
處理錯誤
如果要求失敗,請按照下列步驟操作:
查看錯誤代碼,找出錯誤類型。
檢查標準詳細資料酬載的錯誤代碼。標準詳細資料酬載是一組 Google API 錯誤訊息。以結構化且一致的方式提供錯誤詳細資料。Data Manager API 的每個錯誤可能有多個標準詳細資料酬載訊息。Data Manager API 用戶端程式庫提供輔助方法,可從錯誤中取得標準詳細資料酬載。
無論錯誤代碼為何,我們都建議您檢查並記錄
ErrorInfo、RequestInfo、Help和LocalizedMessage酬載。ErrorInfo包含其他酬載可能沒有的資訊。RequestInfo具有要求 ID,如需聯絡支援團隊,這項資訊會很有幫助。Help和LocalizedMessage包含連結和其他詳細資料,可協助您解決錯誤。
此外,
BadRequest酬載有助於解決INVALID_ARGUMENT錯誤,因為其中提供導致錯誤的欄位資訊。
擷取警告
Data Manager API 會盡可能接受擷取要求。 如果加入非必要資料,這些欄位的驗證失敗不會導致要求失敗。舉例來說,如果購物車項目缺少商家產品 ID,API 會處理其餘要求,並傳回警告。
成功的擷取回應 (HTTP 狀態碼 200) 會在 fieldWarnings 清單中包含這些警告。每個項目都是 FieldWarning 物件,包含下列欄位:
field要求中欄位的位置,採用蛇形命名法路徑語法。
如果路徑指向清單中的項目 (
repeated欄位),其索引會顯示在清單名稱後的方括號 ([...]) 中。舉例來說,
events.events[0].cart_data.items[0].merchant_product_id會找出與要求中第一個事件的購物車資料中第一個項目相關的警告。description說明提供的值為何會導致警告。
reason用於識別警告類型的
WarningReason列舉值。
範例 (含 FieldWarning)
以下是成功擷取要求的範例回應,其中包含警告,因為其中一個購物車項目缺少商家產品 ID。
{
"requestId": "126365e1-16d0-4c81-9de9-f362711e250a",
"fieldWarnings": [
{
"field": "events.events[0].cart_data.items[0].merchant_product_id",
"description": "The merchant product ID is missing in the cart item.",
"reason": "WARNING_REASON_CART_DATA_ITEM_MERCHANT_PRODUCT_ID_MISSING"
}
]
}
標準詳細資料酬載
Data Manager API 最常見的標準詳細資料酬載如下:
BadRequest
如果要求失敗並傳回 BadRequest (HTTP 狀態碼 400),請檢查 INVALID_ARGUMENT 酬載。
BadRequest 訊息表示要求中的欄位含有無效值,或是缺少必填欄位的值。請查看 field_violations 清單中的錯誤訊息
BadRequest,找出有錯誤的欄位。每個 field_violations 項目都包含有助於修正錯誤的資訊:
field要求中欄位的位置,採用蛇形命名法路徑語法。
如果路徑指向清單中的項目 (
repeated欄位),其索引會顯示在清單名稱後的方括號 ([...]) 中。舉例來說,
destinations[0].operating_account.account_id是destinations清單中第一個項目的operating_account內的account_id。description說明值導致錯誤的原因。
reasonErrorReason列舉,例如INVALID_HEX_ENCODING或INVALID_CURRENCY_CODE。
「BadRequest」的例句
以下是發生 INVALID_ARGUMENT 錯誤時的範例回應,其中包含 BadRequest
訊息。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"
}
]
}
]
}
}
以下是 INVALID_ARGUMENT 錯誤的另一個回應範例,其中包含 BadRequest 訊息。在本例中,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 酬載。RequestInfo 包含可專屬識別 API 要求的 request_id。
{
"@type": "type.googleapis.com/google.rpc.RequestInfo",
"requestId": "t-4490c640-dc5d-4c28-91c1-04a1cae0f49f"
}
記錄錯誤或聯絡支援團隊時,請務必提供要求 ID,協助診斷問題。
ErrorInfo
檢查 ErrorInfo 訊息,擷取其他標準詳細資料酬載中可能未擷取的額外資訊。ErrorInfo 酬載包含 metadata 地圖,其中含有錯誤相關資訊。
舉例來說,如果使用 Google Cloud 雲端專案的憑證,但該專案未啟用 Data Manager API,就會導致 ErrorInfo PERMISSION_DENIED 失敗。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"
}
},
...
]
}
}
配額和頻率限制錯誤
如果要求超出專案限制,API 會傳回 RESOURCE_EXHAUSTED 錯誤 (HTTP 狀態碼 429)。ErrorInfo 酬載會在 metadata 對應中提供超出限制的詳細資料:
consumer- 與要求相關聯的 Google Cloud 專案,格式為
projects/PROJECT_NUMBER。 quota_limit- 超出配額限制的名稱,例如
IngestionMutateRequestsPerMinutePerProject或IngestionMutateRequestsPerDayPerProject。您可以根據這個值,判斷應用程式是否超過每分鐘或每日限制。如需完整的限制名稱清單,請參閱「專案限制」。 quota_location- 配額的強制執行位置。如果是 Data Manager API,這項值一律為
global。 quota_metric- 與限制相關聯的指標,例如
datamanager.googleapis.com/ingestion_mutate_requests。 service- 服務名稱,
datamanager.googleapis.com。
以下範例顯示,當要求超過每分鐘的擷取變更要求限制時,系統會傳回 RESOURCE_EXHAUSTED 錯誤回應:
{
"error": {
"code": 429,
"message": "Quota exceeded for quota metric 'Ingestion mutate requests' and limit 'Ingestion mutate requests per minute' of service 'datamanager.googleapis.com' for consumer 'project_number:PROJECT_NUMBER'.",
"status": "RESOURCE_EXHAUSTED",
"details": [
{
"@type": "type.googleapis.com/google.rpc.ErrorInfo",
"reason": "RATE_LIMIT_EXCEEDED",
"domain": "googleapis.com",
"metadata": {
"consumer": "projects/PROJECT_NUMBER",
"quota_limit": "IngestionMutateRequestsPerMinutePerProject",
"quota_location": "global",
"quota_metric": "datamanager.googleapis.com/ingestion_mutate_requests",
"service": "datamanager.googleapis.com"
}
}
]
}
}
Help和LocalizedMessage
檢查 Help 和 LocalizedMessage 酬載,取得文件連結和本地化錯誤訊息,協助您瞭解及修正錯誤。
舉例來說,
如果使用 Google Cloud 雲端專案的憑證
,但未啟用 Data Manager API,就會導致 Help 和 LocalizedMessage 發生 PERMISSION_DENIED 失敗。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...
// If handling a rate limit error, check the exceeded quota limit:
if (errorInfo.Reason == "RATE_LIMIT_EXCEEDED" &&
errorInfo.Metadata.TryGetValue("quota_limit", out string quotaLimit))
{
// Inspect quotaLimit to determine whether it is a per-minute
// or daily limit (for example,
// IngestionMutateRequestsPerMinutePerProject).
}
// 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'...
// If handling a rate limit error, check the exceeded quota limit:
if (errorInfo != null && "RATE_LIMIT_EXCEEDED".equals(errorInfo.getReason())) {
String quotaLimit = errorInfo.getMetadataMap().get("quota_limit");
// Inspect quotaLimit to determine whether it is a per-minute
// or daily limit (for example,
// IngestionMutateRequestsPerMinutePerProject).
}
// 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 = apiException.getErrorDetails().getRequestInfo();
if (requestInfo != null) {
String requestId = requestInfo.getRequestId();
// Log the requestId...
}
...
}
錯誤處理最佳做法
如要建構具備韌性的應用程式,請實作下列最佳做法。
- 傳送前請先驗證
- 建構或變更整合時,請將
validateOnly設為true,以便在擷取資料前,先偵測快速失敗錯誤。 - 檢查錯誤詳細資料
- 請務必尋找標準詳細資料酬載 (例如
BadRequest)。每個標準詳細資料酬載都包含有助於瞭解錯誤原因的資訊。 - 區分用戶端和伺服器錯誤
判斷錯誤是因實作 (用戶端) 問題所致,還是 API (伺服器) 問題所致。
- 用戶端錯誤:例如
INVALID_ARGUMENT、NOT_FOUND、PERMISSION_DENIED、FAILED_PRECONDITION、UNAUTHENTICATED等代碼。這類錯誤需要變更要求,或是應用程式的狀態/憑證。請先解決問題,再重試要求。 - 伺服器錯誤:例如
UNAVAILABLE、INTERNAL、DEADLINE_EXCEEDED、UNKNOWN等代碼。這表示 API 服務可能暫時發生問題。
- 用戶端錯誤:例如
- 實作重試策略
判斷是否可以重試錯誤,並使用重試策略。
只有在發生暫時性伺服器錯誤 (例如
UNAVAILABLE、DEADLINE_EXCEEDED、INTERNAL、UNKNOWN和ABORTED) 和每分鐘頻率限制 (RESOURCE_EXHAUSTED搭配RATE_LIMIT_EXCEEDED) 時,才重試。如要瞭解頻率限制,請檢查
quota_limit中的ErrorInfo:如果限制是每分鐘 (例如
IngestionMutateRequestsPerMinutePerProject),請暫停要求,並使用指數輪詢方式重試,並加入隨機延遲。如果上限是每日 (例如
IngestionMutateRequestsPerDayPerProject),請勿立即重試。暫停處理作業,直到太平洋時間午夜重設每日配額為止。
使用指數輪詢演算法,等待重試間隔時間逐漸增加。以免加重已負載過重的服務負擔。舉例來說,先等待 1 秒,再等待 2 秒,然後等待 4 秒,依此類推,直到達到重試次數上限或總等待時間。
在退避延遲中加入少量隨機「抖動」,避免「雷鳴群」問題,也就是許多用戶端同時重試。
- 詳細記錄
記錄完整錯誤回應,包括所有標準詳細資料酬載,尤其是要求 ID。這項資訊對於偵錯至關重要,如有需要,還可向 Google 支援團隊回報問題。
- 提供使用者意見回饋
根據標準詳細資料酬載中的代碼和訊息,向應用程式使用者提供清楚實用的意見回饋。舉例來說,您可以說「缺少交易 ID」或「找不到目的地帳戶 ID」,而不是只說「發生錯誤」。
只要遵循這些規範,就能有效診斷及處理 Data Manager API 傳回的錯誤,進而打造更穩定且容易使用的應用程式。