APIが429を返したとき、本文のエラーだけでは、いつ次の取得を試せばよいか分からない場合があります。HTTPの応答ヘッダーにはRetry-AfterやContent-Typeなど、本文とは別の情報が入ります。ステータスと組にして確認します。
GASのHTTPResponseではgetAllHeadersで応答ヘッダーを読み取れます。同じ項目に複数の値がある場合は配列になるため、常に1つの文字列だと思って処理しないようにします。ログには必要な項目だけを選びます。
本文とヘッダーを別々に読む
getResponseCodeはHTTPステータス、getContentTextは本文、getAllHeadersはヘッダーを返します。どの情報を表示しているかを名前で分けると、本文のJSONにあるstatusという値とHTTPコードを取り違えにくくなります。
公式のHTTPResponse資料は、複数の値を持つヘッダーを配列として返すことを説明しています。ヘッダー名は返された表記の大文字小文字が一定とは決めず、照合用の名前を小文字にそろえて扱います。
すべてのヘッダーをそのままログへ出すと、Cookieなどの不要な情報も記録する可能性があります。今回はContent-TypeとRetry-Afterだけを取り出し、本文も保存しません。必要になった項目を個別に追加する形にします。
必要な項目を配列へそろえるコード
実際の通信をしなくても形を確認できるよう、下の実行例は架空の応答オブジェクトを使います。実応答でも同じgetResponseCodeとgetAllHeadersを呼ぶ関数です。
function summarizeResponseHeaders(response) {
const normalized = Object.create(null);
const headers = response.getAllHeaders();
Object.keys(headers).forEach(function (name) {
const key = name.toLowerCase();
if (!['content-type', 'retry-after'].includes(key)) return;
const value = headers[name];
const values = Array.isArray(value) ? value : [value];
normalized[key] = (normalized[key] || []).concat(values.map(String));
});
return {
httpStatus: response.getResponseCode(),
contentType: normalized['content-type'] || [],
retryAfter: normalized['retry-after'] || []
};
}
function previewResponseHeaders() {
const sample = {
getResponseCode: () => 429,
getAllHeaders: () => ({
'Content-Type': 'application/json', 'retry-after': ['30'],
'Set-Cookie': '掲載しない架空の値'
})
};
console.log(JSON.stringify(summarizeResponseHeaders(sample)));
}
結果はhttpStatus:429、contentType:["application/json"]、retryAfter:["30"]です。Set-Cookieは結果に入りません。文字列と配列を同じ形にそろえるので、後続で要素数を確認できます。
Retry-Afterをそのまま待機秒数にしない
RFC 9110にあるように、Retry-Afterは秒数だけでなくHTTP日時で示される場合があります。parseIntをかけるだけでは、日時の文字列を待機時間として扱えません。形式を判定し、使用するAPIの再試行条件と合わせて解釈します。
複数の値が返った場合も、先頭だけを根拠なく採用しません。中継するサーバーなどの影響を含め、公式のAPI資料と実際の応答を確認します。値がない場合は0秒とみなさず、「情報なし」として別に扱います。
ヘッダーを読めたことと、要求を再送してよいことも別です。投稿作成やメール送信の結果が不明なら、送信先に結果があるかを先に調べます。読み取りの取得と、重複で困る更新を同じ再試行処理に入れないようにします。
実応答へつなぐときの確認
UrlFetchApp.fetchで失敗ステータスも調べたい場合は、muteHttpExceptions:trueを指定する方法があります。ただし通信自体の例外まで、すべてHTTPResponseへ変わるわけではありません。応答を受け取った場合と、通信例外を分けます。
muteHttpExceptionsは公式のUrlFetchAppで確認できます。掲載コードはローカルの代用応答で、大文字小文字、配列、未存在、不要ヘッダーの除外を確認しました。実際のAPI通信や待機・再送は実行していません。
