HTTPの202 Accepted、依頼の受付と処理の完了を分けて確かめる

クリーム色の長い溝が二つの受け皿へ分かれ、途中の紫色の部品の先に、完成した緑色の形と分離した二片がある。

画像変換や集計をAPIへ頼み、HTTPの202 Acceptedが返ってきたとします。要求は受け付けられていますが、画像や集計結果がもう使えるとは限りません。時間のかかる処理では、「依頼を受けた」と「依頼どおりの結果ができた」を別々に知らせる必要があります。

202を正しく読むには、最初の応答だけでなく、その後どこから何を確認するのかまで見ます。ここでは架空の画像変換サービスを例に、HTTPの応答と処理状態の役割を分けます。

簡単にまとめると?

  • 202は処理を受け付けたが、まだ完了していないという応答です。将来の成功を保証するコードではありません。
  • 確認用URLをGETして200が返っても、その本文が「処理中」なら変換は未完了です。HTTP応答とジョブ状態を分けて読みます。
  • Prefer: respond-asyncやwaitはクライアントの希望です。完了通知の方法や重複処理の防止まで、自動で決まるわけではありません。

接続を閉じても、仕事は残る

画像変換に数十秒かかる場合、処理が終わるまで最初のHTTP接続を待たせる設計もできます。一方、先に依頼を受け付け、変換を別の処理へ引き渡す設計なら、最初の応答を早く返せます。202は、処理が終わるまで接続を維持せずに要求を受け付けるために使えます。[1]

ここで保証が増えすぎないように注意します。RFC 9110の202は、実際の処理段階で許可されないなどの理由から、最終的に実行されない可能性も含みます。受付後に入力の内容が不適切だと分かる、変換処理が失敗する、といった結果を、最初の202だけで排除することはできません。

また、最初の202が後から200に書き換わって届く仕組みではありません。HTTPは、その非同期処理の最終ステータスを同じ応答へ後で送り直す仕組みを202に組み込んでいません。最終結果を知る経路は別に必要です。[1、15.3.3節]

確認URLの200は、変換完了とは限らない

説明用に、POST /conversionsの応答本文が、受付ID「job-42」と確認先 /jobs/job-42 を返す設計を考えます。確認先をGETすると、queued(待機中)、running(処理中)、succeeded(成功)、failed(失敗)のいずれかを本文に載せるものとします。これらの状態名やURL構造は本稿の例であり、202の標準で決められた共通形式ではありません。

GET /jobs/job-42が200 OKで、本文にrunningが入っているなら、「現在のジョブ情報を取得する要求」が成功したということです。画像変換が成功したという意味にはなりません。同じように、失敗を記録したジョブ情報を正常に取得できた場合も、この設計ではHTTPは200、ジョブはfailedになります。

説明用の非同期処理。最初はPOSTに202で受付。確認先GETは200を返し、本文のジョブ状態が待機中、処理中、成功または失敗へ進む。GETの200だけでは成功とは判定しない。
受付、状態取得、処理結果を分けた独自の設計例。状態名と遷移は説明用で、HTTPがこの形式を必須にしているわけではない。

この例で結果を使う条件は、succeededを確認し、返された結果URLから期待する成果物を取得できることです。確認用のURLが返った段階、ジョブの成功を読んだ段階、画像を実際に取得した段階を記録しておくと、「受付画面は出たがファイルがない」ときに調べる場所を絞れます。

非同期で返してほしい、という希望を伝える

クライアントは、RFC 7240のPrefer: respond-asyncで非同期の応答を希望できます。サーバーは、その希望に応じて202を返す設計にできますが、Preferは必須の要求ではありません。対応していない希望は無視して処理を続けるため、ヘッダーを付けただけで非同期動作を保証できません。[2、2節・4.1節]

同じ仕様のwaitを使い、Prefer: respond-async, wait=10と伝える例もあります。サーバーが受信後の処理に10秒より長くかかるなら、非同期応答へ切り替える判断材料になります。しかしこれは、通信経路も含めて必ず10秒以内に応答が届くという期限保証ではありません。ネットワークや中継処理の時間もあり、wait自体も無視されることがあります。[2、4.3節]

RFC 7240には、202とLocationを組み合わせた応答例があります。ただし、以後の最終結果をどの手順で取得するかは実装に委ねられています。[2、4.1節] 202なら必ず同じ形のLocationがある、あるいは自動的に完了通知が来る、と仮定せず、対象APIの本文や契約を確認します。

確認の繰り返しと、仕事の再依頼を混ぜない

本稿の例で処理中だったとき、次に行うのは確認URLのGETです。元のPOSTをもう一度送ると、サービスが重複を識別しない設計なら、別の変換依頼を作る可能性があります。進み具合を読む操作と、新しい仕事を依頼する操作を分けることが大切です。

さらに、最初の応答を受け取る前に通信が途切れた場合は、受付済みなのかがクライアントから分かりません。「202を見ていない」ことは「サーバーが受け付けていない」証拠にはなりません。こうした再送を支えるには、サービスが定める依頼識別子や重複検出の仕組みを使う必要があり、202というコードだけでは補えません。

待ち時間や状態記録の保持期限も、実際のAPIに合わせます。確認先が後で404になったときに、「依頼が存在しなかった」「保存期限が切れた」「参照権限がない」などを区別できるかは、そのAPIの設計次第です。本稿の小さな状態モデルは、その挙動まで保証するものではありません。

利用側で決めておく確認の終点

非同期APIを使う前に、受付ID、確認先、終了状態、成功時の結果、失敗時の理由をどう読むかを揃えます。そのうえで、どの間隔で確認するか、いつ待つのをやめるか、途中でアプリを閉じた後も同じ仕事を追えるかを、サービスの仕様と照合します。

202は、長い仕事を一つの接続に閉じ込めないための入り口です。依頼の成功を確かめる終点ではありません。受付、状態の取得、結果の利用という三つを分けることで、画面表示と実際の進捗を対応させやすくなります。

出典

  1. RFC 9110: HTTP Semantics(2022年6月、STD 97)、15.3.3節202 Accepted。再送の一般条件は9.2.2節。
  2. RFC 7240: Prefer Header for HTTP(2014年6月、Standards Track)、2節Prefer、4.1節respond-async、4.3節wait。