web-dev-qa-db-ja.com

複数のアクションが異なるステータスで終了した場合に返すHTTPステータスコードは何ですか?

ユーザーがサーバーに1つのHTTPリクエストで複数のアクションを実行するように要求できるAPIを構築しています。結果は、アクションごとに1つのエントリーを持つJSON配列として返されます。

これらの各アクションは、互いに独立して失敗または成功する場合があります。たとえば、最初のアクションが成功し、2番目のアクションへの入力の形式が不十分で検証に失敗し、3番目のアクションが予期しないエラーを引き起こす可能性があります。

アクションごとに1つのリクエストがあった場合、ステータスコード200、422、500をそれぞれ返します。しかし、リクエストが1つしかない場合、どのステータスコードを返す必要がありますか?

いくつかのオプション:

  • 常に200を返し、より詳細な情報を本文に含めます。
  • リクエストに複数のアクションがある場合にのみ、上記のルールに従うことがありますか?
  • すべてのリクエストが成功した場合は200を返し、それ以外の場合は500(または他のコード)を返しますか?
  • アクションごとに1つのリクエストを使用し、余分なオーバーヘッドを受け入れます。
  • 何か完全に違う?
73
Anders

短く直接的な答え

リクエストはtasksのリストの実行について話すので(タスクはここで言うリソースです)、タスクグループが実行に進められた場合(つまり、実行結果に関係なく) )の場合、応答ステータスは200 OKになります。それ以外の場合、検証の失敗タスクオブジェクトのなどのタスクグループの実行を妨げる問題が発生した場合、または必要なサービスが利用できない場合など、応答ステータスは次のようになります。エラー。その後、タスクの実行が開始されると、実行するタスクがリクエストの本文にリストされているのを見て、実行結果がレスポンスの本文にリストされると思います。


長く哲学的な答え

このジレンマが発生しているのは、HTTPの設計目的から逸脱しているためです。リソースを管理するためにそれを操作するのではなく、リモートメソッド呼び出しの手段として使用します(奇妙ではありませんが、先入観のないスキームではうまく機能しません)。

上記のことを言い、この答えを長い説得力のあるガイドに変える勇気なしに、以下はリソース管理アプローチに準拠したURIスキームです。

  • /tasks
    • GETは、ページ付けされたすべてのタスクをリストします
    • POSTは単一のタスクを追加します
  • /tasks/task/[id]
    • GETは単一のタスクの状態オブジェクトで応答します
    • DELETEタスクをキャンセル/削除します
  • /tasks/groups
    • GETは、ページ付けされたすべてのタスクグループを一覧表示します
    • POSTはタスクのグループを追加します
  • /tasks/groups/group/[id]
    • GETはタスクグループの状態で応答します
    • DELETEタスクグループをキャンセル/削除します

この構造は、リソースについて何をすべきかではなく、リソースについて話します。リソースで行われているのは、別のサービスの懸念です。

もう1つの重要なポイントは、HTTPリクエストハンドラーで長時間ブロックしないことをお勧めします。 UIと同様に、HTTPインターフェースは応答性が高くなければなりません-タイムスケールでは数桁遅い(このレイヤーはIOを扱うため)。

リソースを厳密に管理するHTTPインターフェースの設計に移行することは、ボタンがクリックされたときにUIスレッドから作業を移動することと同じくらい困難です。 HTTPサーバーは、リクエストハンドラでタスクを実行するのではなく、他のサービスと通信してタスクを実行する必要があります。これは浅い実装ではなく、方向転換です。


このようなURIスキームの使用例

単一のタスクを実行して進行状況を追跡する:

  • POST /tasks実行するタスク
    • GET /tasks/task/[id]まで、応答オブジェクトcompletedは現在のステータス/進行状況を表示しながら正の値をとります

単一のタスクを実行し、その完了を待つ:

  • POST /tasks実行するタスク
    • GET /tasks/task/[id]?awaitCompletion=truecompletedが正の値になるまで(タイムアウトしている可能性が高いため、これをループする必要があります)

タスクグループの実行と進行状況の追跡:

  • POST /tasks/groups実行するタスクのグループ
    • GET /tasks/groups/group/[groupId]応答オブジェクトまでcompletedプロパティに値があり、個々のタスクのステータスを示します(たとえば、5つのうち3つのタスクが完了)

タスクグループの実行を要求し、その完了を待機しています:

  • POST /tasks/groups実行するタスクのグループ
    • GET /tasks/groups/group/[groupId]?awaitCompletion=trueは、完了を示す結果で応答するまで(タイムアウトになる可能性が高いため、ループする必要があります)
22
Ben Barkay

私の投票は、これらのタスクを個別のリクエストに分割することです。しかし、あまりにも多くの往復が問題になる場合、私は遭遇しました HTTP応答コード207-マルチステータス

このリンクからコピー/貼り付け:

マルチステータスレスポンスは、複数のステータスコードが適切な状況で、複数のリソースに関する情報を伝えます。デフォルトのマルチステータスレスポンスボディは、 'multistatus'ルート要素を持つtext/xmlまたはapplication/xml HTTPエンティティです。その他の要素には、メソッドの呼び出し中に生成された200、300、400、および500シリーズのステータスコードが含まれます。 100シリーズのステータスコードは、「応答」XML要素に記録してはいけません(SHOULD NOT)。

「207」は全体的な応答ステータスコードとして使用されますが、受信者はメソッド実行の成功または失敗に関する詳細情報について、マルチステータス応答本文の内容を調べる必要があります。応答は、成功、部分的な成功、および失敗の状況で使用される場合があります。

87
neilsimp1

マルチステータスはオプションですが、すべてのリクエストが成功した場合は200(すべて正常)を返し、それ以外の場合はエラー(500またはおそらく207)を返します。

標準ケースは通常200である必要があります-すべてが機能します。そして、クライアントはそれをチェックするだけでよいはずです。エラーが発生した場合にのみ、500(または207)を返すことができます。 207は少なくとも1つのエラーの場合には有効な選択だと思いますが、パッケージ全体を1つのトランザクションとして表示する場合は、500を送信することもできます。-クライアントはエラーメッセージをいずれかの方法で解釈する必要があります。

常に207を送信するわけではないのですか?-標準のケースは簡単で標準的なはずです。例外的なケースは例外的な場合があります。例外的な状況で必要な場合は、クライアントは応答本文を読んでさらに複雑な決定を行うだけで済みます。

23
Falco

1つのオプションは、常にステータスコード200を返し、JSONドキュメント本文で特定のエラーを返すことです。これは、一部のAPIが設計されている方法とまったく同じです(常にステータスコード200を返し、本文でエラーを送出します)。さまざまなアプローチの詳細については、 http://archive.oreilly.com/pub/post/restful_error_handling.html を参照してください。

13
BigONotation

Neilsimp1は正しいと思いますが、206 - Acceptedを送信して後でデータを処理できるように、送信されるデータを再設計することをお勧めします。おそらくコールバックを使用します。

単一のリクエストで複数のアクションを送信しようとすることの問題は、各アクションが独自の「ステータス」を持つ必要があるという事実です

CSVのインポートを検討しています(OPの詳細はわかりませんが、シンプルなバージョンです)。 POST CSVを取得して206を取得します。その後、CSVをインポートして、行ごとのエラーを示すURLに対してGET(200)を使用してインポートのステータスを取得できます。

POST /imports/ -> 206
GET  /imports/1 -> 200
GET  /imports/1/errors -> 200 -> Has a list of errors

これと同じパターンは、多くのバッチ処理に適用できます

POST /operations/ -> 206
GET  /operations/1 -> 200
GET  /operations/1/errors -> 200 - > Has a list of errors.

POSTを処理するコードは、操作データの形式が有効であることを確認するだけで済みます。その後、後で操作が実行される可能性があります。バックグラウンドワーカーでは、簡単にスケーリングできます。たとえば、必要なときにいつでも操作のステータスを確認できます。一連の操作が完了したことを知る必要がある場合は、ポーリングやコールバック、ストリームなどを使用して、必要に応じて対処できます。

4
coteyr

ここにはすでに多くの良い答えがありますが、1つの側面がありません:

クライアントが期待する契約は何ですか?

HTTPリターンコードは、少なくとも成功/失敗の区別を示す必要があるため、「貧乏人の例外」の役割を果たします。その場合、200は「契約が完全に履行されたこと」を意味し、4xxまたは5xxは履行の失敗を示します。

単純に、私はあなたの複数アクション要求の契約が「すべての私のタスクを実行する」ことを期待し、それらの1つが失敗した場合、要求は(完全に)成功しませんでした。通常、クライアントとして私は200を「すべて問題ない」と理解し、400および500ファミリーのコードは、(部分的な)障害の結果について考えることを強いられます。したがって、「すべてのタスク完了」には200を使用し、部分的な失敗の場合は500に加えて説明的な応答を使用します。

別の架空の契約は、「すべてのアクションを実行してみる」かもしれません。その後、アクションの一部(または一部)が失敗した場合、それは完全に契約と一致しています。したがって、常に200を返し、個々のタスクの成功/失敗の情報を見つける結果ドキュメントを返します。

それで、あなたが従いたい契約は何ですか?どちらも有効ですが、最初の方(すべてが行われた場合は200のみ)は私にとってより直感的であり、一般的なソフトウェアパターンに沿った方が優れています。また、サービスがすべてのタスクを完了した(できれば)大多数のケースでは、クライアントがそのケースを検出するのは簡単です。

最後の重要な側面:契約の決定をクライアントにどのように伝えますか?例えば。 Javaでは、「doAll()」や「tryToDoAll()」などのメソッド名を使用します。 HTTPでは、クライアント開発者が名前を見て、読んで、理解することを期待して、それに応じてエンドポイントURLに名前を付けることができます(私はそれには賭けません)。驚きが最も少ない契約を選択するもう1つの理由。

2
Ralf Kleberhoff

回答:

アクションごとに1つのリクエストを使用し、余分なオーバーヘッドを受け入れます。

ステータスコードは、1つの操作のステータスを示します。したがって、リクエストごとに1つの操作を行うことは理にかなっています。

複数の独立した操作は、要求と応答のモデルとステータスコードの基になっているプリンシパルを壊します。あなたは自然と戦っています。

HTTP/1.1とHTTP/2では、HTTPリクエストのオーバーヘッドがはるかに低くなっています。独立したリクエストのバッチ処理が推奨される状況は非常に少ないと思います。


とはいえ、

(1)PATCHリクエスト( RFC 5789 )で複数の変更を行うことができます。ただし、これには変更が独立していないことが必要です。それらはアトミックに適用されます(オールオアナッシング)。

(2)207マルチステータスコードを指摘する人もいます。ただし、これはHTTPの拡張であるWebDAV( RFC 4918 )に対してのみ定義されます。

207(マルチステータス)ステータスコードは、複数の独立した操作のステータスを提供します(詳細については、セクション13を参照してください)。

...

マルチステータスレスポンスは、複数のステータスコードが適切な状況で、複数のリソースに関する情報を伝えます。 'multistatus'ルート[XML]要素は、0個以上の 'response'要素を任意の順序で保持し、それぞれに個別のリソースに関する情報が含まれます。

207 WebDAV XML応答は、非WebDAV APIのアヒルのように奇妙です。これを行わないでください。

0
Paul Draper

1つのリクエストに複数のアクションが本当に必要な場合は、すべてのアクションをトランザクションのバックエンドでラップしてみませんか?このようにして、すべてが成功するか、すべてが失敗します。

APIを使用するクライアントとして、API呼び出しの完全な成功または失敗に対処できます。結果として生じる可能性のあるすべての状態を処理する必要があるため、部分的な成功を処理することは困難です。

0
Dean