REST API-単一リクエストでの一括作成または更新[終了]


92

2つのリソースがBinderありDocDocBinderがそれぞれ独立していることを意味する関連関係があると仮定します。Doc属している場合Binderと属していない場合がBinderあり、空の場合があります。

私は、ユーザーがのコレクションを送ることを可能にするREST APIを設計したい場合はDocSを、中のA SINGLE要求、次のように:

{
  "docs": [
    {"doc_number": 1, "binder": 1}, 
    {"doc_number": 5, "binder": 8},
    {"doc_number": 6, "binder": 3}
  ]
}

の各ドキュメントについてdocs

  • doc存在する場合は、それをBinder
  • 場合はdoc存在しない、それを作成し、それを割り当てます

これがどのように実装されるべきかについて私は本当に混乱しています:

  • 使用するHTTPメソッドは?
  • どの応答コードを返さなければなりませんか?
  • これはRESTにも適格ですか?
  • URIはどのように見えますか?/binders/docs
  • バルクリクエストを処理し、いくつかのアイテムでエラーが発生したが、他のアイテムは通過した場合 どんな応答コードが返されなければなりませんか?一括操作はアトミックにする必要がありますか?

回答:


58

POSTまたはPATCHメソッドを使用してこれを処理できると思います。これは、通常、この方法で設計されているためです。

  • POSTメソッドの使用は、通常、リストリソースで使用されるときに要素を追加するために使用されますが、このメソッドのいくつかのアクションをサポートすることもできます。この回答を参照してください:RESTリソースコレクションを更新する方法。入力に対してさまざまな表現形式をサポートすることもできます(それらが配列または単一の要素に対応している場合)。

    この場合、更新を説明するためにフォーマットを定義する必要はありません。

  • PATCH対応するリクエストは部分的な更新に対応するためメソッドの使用も適しています。RFC5789(http://tools.ietf.org/html/rfc5789)によると:

    ハイパーテキスト転送プロトコル(HTTP)を拡張するいくつかのアプリケーションには、部分的なリソース変更を行う機能が必要です。既存のHTTP PUTメソッドは、ドキュメントの完全な置換のみを許可します。この提案は、既存のHTTPリソースを変更するために、新しいHTTPメソッドPATCHを追加します。

    この場合、部分的な更新を説明するためにフォーマットを定義する必要があります。

私は、この場合だと思います、POSTそしてPATCHあなたが本当に各要素に対して実行する操作を記述する必要はありませんので、非常によく似ています。送信する表現の形式に依存すると思います。

の場合PUTは少し明確ではありません。実際、メソッドを使用するPUT場合は、リスト全体を提供する必要があります。実際のところ、リクエストで提供される表現は、リストリソース1の代わりになります。

リソースパスに関しては2つのオプションがあります。

  • ドキュメントリストのリソースパスの使用

この場合、リクエストで提供する表現に、バインダーとドキュメントのリンクを明示的に提供する必要があります。

これはこのためのサンプルルートです/docs

このようなアプローチの内容は、メソッドの場合がありますPOST

[
    { "doc_number": 1, "binder": 4, (other fields in the case of creation) },
    { "doc_number": 2, "binder": 4, (other fields in the case of creation) },
    { "doc_number": 3, "binder": 5, (other fields in the case of creation) },
    (...)
]
  • バインダー要素のサブリソースパスを使用する

さらに、ドキュメントとバインダー間のリンクを記述するためにサブルートを活用することを検討することもできます。ドキュメントとバインダーの関連付けに関するヒントを、要求コンテンツ内で指定する必要がなくなりました。

これはこのためのサンプルルートです/binder/{binderId}/docs。この場合、メソッドを使用してドキュメントのリストを送信するPOSTか、ドキュメントが存在しない場合は、ドキュメントを作成した後にPATCHIDを付けてバインダーにドキュメントを添付binderIdします。

このようなアプローチの内容は、メソッドの場合がありますPOST

[
    { "doc_number": 1, (other fields in the case of creation) },
    { "doc_number": 2, (other fields in the case of creation) },
    { "doc_number": 3, (other fields in the case of creation) },
    (...)
]

応答に関しては、応答のレベルと返すエラーを定義するのはあなた次第です。ステータスレベル(グローバルレベル)とペイロードレベル(シンナーレベル)の2つのレベルが表示されます。また、リクエストに対応するすべての挿入/更新をアトミックにする必要があるかどうかを定義するのはユーザーの責任です。

  • 原子

この場合、HTTPステータスを利用できます。すべてがうまくいくと、ステータスが表示されます200。そうでない場合は、400提供されたデータが正しくない(たとえば、バインダーIDが無効である)などの別のステータス。

  • 非アトミック

この場合、ステータス200が返され、何が行われ、最終的にどこでエラーが発生するかを説明するのは、応答の表現次第です。ElasticSearchのREST APIには、一括更新用のエンドポイントがあります。これにより、このレベルでいくつかのアイデアが得られます:http : //www.elasticsearch.org/guide/en/elasticsearch/guide/current/bulk.html

  • 非同期

提供されたデータを処理するための非同期処理を実装することもできます。この場合、HTTPステータスの戻りはになります202。クライアントは、何が起こるかを確認するために、追加のリソースをプルする必要があります。

終了する前に、OData仕様がナビゲーションリンクという名前の機能を持つエンティティ間の関係に関する問題に対処していることにも注目したいと思います。多分これを見ていただけますか;-)

次のリンクも役立ちます:https : //templth.wordpress.com/2014/12/15/designing-a-web-api/

それがあなたを助けることを願っています、ティエリー


私は質問に従っています。ネストされたサブリソースのないフラットルートを選択しました。すべてのドキュメントをGET /docs取得するには、特定のバインダー内のすべてのドキュメントを呼び出して取得しますGET /docs?binder_id=x。リソースのサブセットを削除するには、私は呼ぶだろうDELETE /docs?binder_id=xか、私が呼び出す必要がありますDELETE /docs{"binder_id": x}リクエストボディで?PATCH /docs?binder_id=xバッチ更新に使用したりPATCH /docs、ペアを渡したりすることはありますか?
アンディFusniak

34

おそらく、POSTまたはPATCHを使用する必要があります。これは、複数のリソースを更新および作成する単一の要求がべき等であることはほとんどないためです。

行うことPATCH /docsは間違いなく有効なオプションです。特定のシナリオでは、標準のパッチ形式を使用するのが難しい場合があります。わからない

200を使用できます。207- マルチステータスも使用できます。

これはRESTfulな方法で行うことができます。私の意見では、キーは、更新/作成するドキュメントのセットを受け入れるように設計されたリソースを用意することです。

PATCHメソッドを使用する場合、操作はアトミックである必要があると思います。つまり、207ステータスコードを使用せず、応答本文で成功と失敗を報告します。POST操作を使用する場合、207のアプローチが実行可能です。成功した操作と失敗した操作を伝えるために、独自の応答本文を設計する必要があります。標準化されたものは知りません。


どうもありがとうございます。This can be done in a RESTful way[更新を意味していますし、個別に行う必要があります作成しますか?
Sam R.

1
@norbertpyリソースに対して何らかの書き込み操作を実行すると、他のリソースが単一の要求から更新および作成される可能性があります。RESTにはそれに関する問題はありません。一部のフレームワークは、HTTP要求をマルチパートドキュメントにシリアル化し、シリアル化されたHTTP要求をバッチとして送信することにより、一括操作を実装するため、フレーズを選択しました。そのアプローチはリソース識別REST制約に違反すると思います。
Darrel Miller

19

PUT ING

PUT /binders/{id}/docs 単一のドキュメントを作成または更新し、バインダーに関連付ける

例えば:

PUT /binders/1/docs HTTP/1.1
{
  "docNumber" : 1
}

PATCHの ING

PATCH /docs ドキュメントが存在しない場合はドキュメントを作成し、バインダーに関連付けます

例えば:

PATCH /docs HTTP/1.1
[
    { "op" : "add", "path" : "/binder/1/docs", "value" : { "doc_number" : 1 } },
    { "op" : "add", "path" : "/binder/8/docs", "value" : { "doc_number" : 8 } },
    { "op" : "add", "path" : "/binder/3/docs", "value" : { "doc_number" : 6 } }
] 

追加の洞察を後で含めますが、それまでの間、必要に応じて、RFC 5789RFC 6902、およびWilliam Durand's Pleaseを参照してください。馬鹿なブログエントリのようにパッチを適用しないでください


2
時々クライアントはバルク操作を必要とし、リソースがそこにあるかどうかを気にしません。質問で述べたように、クライアントはの束を送信してにdocs関連付けたいと考えていますbinders。クライアントは、バインダーが存在しない場合はバインダーを作成し、存在する場合は関連付けを行います。ONE SINGLE BULKリクエスト。
Sam R.

12

私が働いていたプロジェクトでは、「バッチ」リクエストと呼ばれるものを実装することでこの問題を解決しました。/batchjsonを受け入れるパスを次の形式で定義しました。

[  
   {
      path: '/docs',
      method: 'post',
      body: {
         doc_number: 1,
         binder: 1
      }
   },
   {
      path: '/docs',
      method: 'post',
      body: {
         doc_number: 5,
         binder: 8
      }
   },
   {
      path: '/docs',
      method: 'post',
      body: {
         doc_number: 6,
         binder: 3
      }
   },
]

応答のステータスコードは207(マルチステータス)で、次のようになります。

[  
   {
      path: '/docs',
      method: 'post',
      body: {
         doc_number: 1,
         binder: 1
      }
      status: 200
   },
   {
      path: '/docs',
      method: 'post',
      body: {
         error: {
            msg: 'A document with doc_number 5 already exists'
            ...
         }
      },
      status: 409
   },
   {
      path: '/docs',
      method: 'post',
      body: {
         doc_number: 6,
         binder: 3
      },
      status: 200
   },
]

この構造にヘッダーのサポートを追加することもできます。リクエスト間でバッチで使用する変数であると判明したものを実装しました。つまり、あるリクエストからのレスポンスを別のリクエストへの入力として使用できます。

FacebookとGoogleには同様の実装があります:
https : //developers.google.com/gmail/api/guides/batch
https://developers.facebook.com/docs/graph-api/making-multiple-requests

同じ呼び出しでリソースを作成または更新する場合は、ケースに応じてPOSTまたはPUTを使用します。ドキュメントが既に存在する場合、ドキュメント全体を次のようにしますか?

  1. 送信したドキュメントで置き換えられますか(つまり、リクエストで欠落しているプロパティは削除され、既存の上書きされます)?
  2. 送信したドキュメントとマージされますか(つまり、リクエストで欠落しているプロパティは削除されず、既存のプロパティは上書きされます)?

代替1の動作が必要な場合はPOSTを使用し、代替2の動作が必要な場合はPUTを使用する必要があります。

http://restcookbook.com/HTTP%20Methods/put-vs-post/

人々がすでに提案したように、パッチを使用することもできますが、私はAPIをシンプルに保ち、不要な動詞を使用しないことをお勧めします。


5
概念実証だけでなく、GoogleおよびFacebookのリンクに対するこの回答のように。ただし、POSTまたはPUTに関する最後の部分には同意しません。この回答で言及した2つのケースでは、最初の回答はPUT、2番目の回答はPATCHです。
RayLuo 2017年

@ RayLuo、POSTとPUTに加えてPATCHが必要な理由を説明できますか?
David Berg

2
それがPATCHが発明された目的だからです。この定義を読んで、PUTとPATCHが2つの箇条書きにどのように一致するかを確認できます。
RayLuo 2017年

@ DavidBerg、Googleはバッチリクエストを処理する別のアプローチを好んだようです。つまり、各サブリクエストのヘッダーと本文を、のような境界でメインリクエストの対応する部分に分離し--batch_xxxxます GoogleとFacebookのソリューション間に決定的な違いはありますか?さらに、「1つの要求からの応答を別の要求への入力として使用する」については、非常に興味深いように思えますが、詳細を共有していただけませんか。またはどのようなシナリオを使用する必要がありますか?
ヤン
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.