web-dev-qa-db-ja.com

POSTのべき等ではない結果(RESTful API))

私の現在のアプローチが理にかなっているのか、それを行うためのより良い方法があるのか​​と思います。

新しいオブジェクトを作成し、サーバーにそれらのオブジェクトにIDを割り当てさせたい状況が複数あります。 POSTリクエストを送信するのが最も適切な方法のようです。ただし、POSTはべき等ではないため、リクエストが失われ、再度送信すると作成される可能性がありますAPIはモバイルネットワーク経由でアクセスされることが多いため、リクエストが失われることもよくあります。

その結果、全体を2つのステップのプロセスに分割することにしました。最初にPOSTリクエストを送信して、新しいオブジェクトのURIをLocationヘッダーに返す新しいオブジェクトを作成します。次に、指定されたLocationに対してべき等PUTリクエストを実行して、新しいオブジェクトにデータを入力します。新しいオブジェクトが24時間以内に生成されない場合、サーバーはある種のバッチジョブによってそれを削除することがあります。

それは合理的に聞こえますか、それともより良いアプローチがありますか?

ありがとう

31
mibollma

POST作成とPUT作成の唯一の利点は、サーバーによるIDの生成です。私はべき等性の欠如(そして重複または空のオブジェクトを削除する必要性)の価値があるとは思いません。

代わりに、URLに [〜#〜] uuid [〜#〜] を含むPUTを使用します。 UUIDジェネレーターのおかげで、あなたは ほぼ確実 クライアント側で生成するIDがサーバー側で一意になることを確認します。

35
Aurélien

それはすべて依存します。まず、URI、リソース、および表現について話し、オブジェクトについては気にしないでください。

POSTメソッドは、べき等ではないリクエスト、または副作用のあるリクエスト用に設計されていますが、 べき等のリクエストに使用できます

on POST to form data to/some_collection /

normalize the natural key of your data (Eg. "lowercase" the Title field for a blog post)
calculate a suitable hash value (Eg. simplest case is your normalized field value)
lookup resource by hash value
if none then
    generate a server identity, create resource
        Respond =>  "201 Created", "Location": "/some_collection/<new_id>" 
if found but no updates should be carried out due to app logic
        Respond => 302 Found/Moved Temporarily or 303 See Other 
        (client will need to GET that resource which might include fields required for updates, like version_numbers)
if found but updates may occur
   Respond => 307 Moved Temporarily, Location: /some_collection/<id> 
   (like a 302, but the client should use original http method and might do automatically) 

適切なハッシュ関数は、いくつかの連結されたフィールドと同じくらい簡単かもしれません。または、大きなフィールドや値の場合、切り捨てられたmd5関数を使用できます。詳細については、[ハッシュ関数] 2 を参照してください。

私はあなたを仮定しました:

  • ハッシュ値とは異なるID値が必要
  • iDに使用されるデータフィールドは変更できません
15
bdargan

サーバー、アプリケーション、専用の要求と応答でIDを生成する方法は非常に優れています。一意性は非常に重要ですが、求婚者などのクライアントは、成功するまで、または受け入れようとする失敗を受け取るまで(可能性は低いですが)、要求を繰り返し続けます。したがって、どこかから一意性を取得する必要があり、2つのオプションしかありません。 GUIDを使用するクライアント、またはサーバーが推奨するように。私はたまたまサーバーオプションが好きです。Seed列衝突のリスクがなく、すぐに利用できる一意性のソースです。2000年のラウンドで、「HTTPによるシンプルな信頼性の高いメッセージング」のようなこのソリューションを提唱する記事を読んだので、これは実際の問題に対する確立されたアプローチです。

読書RESTもの、あなたはたくさんのティーンエイジャーがちょうどエルビスの大邸宅を継承したと思っていても許されるかもしれません。彼らは家具を再配置する方法を興奮して議論していて、彼らは彼らがかもしれないアイデアにヒステリックです自宅から何かを持ち込む必要があります。POST)を使用することをお勧めしますそこにあるため、非べき等のリクエスト。

実際には、おそらく APIへのすべての安全でないリクエストがべき等であることを確認したい ですが、ID生成リクエストの必要な例外はありますが、指摘したとおり、これは重要ではありません。 IDの生成は安価であり、未使用のIDは簡単に破棄されます。 RESTへの同意として、POSTで新しいIDを取得することを忘れないでください。そのため、それはキャッシュされず、あらゆる場所で繰り返されません。

べき等の意味についての無菌の議論 に関して、私はそれがすべてである必要があると言います。連続するリクエストは追加の効果を生成せず、最初に処理されたリクエストと同じ応答を受信する必要があります。これを実装するには、すべてのサーバー応答を保存してそれらを再生できるようにし、IDはリソースだけでなくアクションを識別します。エルビスの邸宅から追い出されますが、防弾APIが作成されます。

4
bbsimonbb

使用するHTTPメソッドに関係なく、クライアント側で一時的に(リクエストチェックシステムの一部として)、または永続的なサーバーIDとして一意の識別子を生成せずにべき等のリクエストを行うことは理論的に不可能です。 HTTPリクエストが失われることで複製が作成されることはありませんが、リクエストがサーバーに正常に到達する可能性はありますが、応答がクライアントに返されることはありません。

エンドクライアントが重複を簡単に削除でき、それらが固有のデータ競合を引き起こさない場合は、アドホックな重複防止システムを開発するのに十分な量ではないでしょう。要求に対してPOST=を使用し、HTTPヘッダーの201ステータスと応答の本文でサーバーが生成した一意のIDをクライアントに送り返します。重複が頻繁に発生することを示すデータがある場合発生または重複が原因で重大な問題が発生する場合は、PUTを使用してクライアント側で一意のIDを作成します。クライアントが作成したIDをデータベースIDとして使用します-サーバーで追加の一意のIDを作成してもメリットはありません。

3
Chris Broski

しかし、今、失われる可能性のある2つの要求がありますか?また、POSTは引き続き繰り返すことができ、別のリソースインスタンスを作成します。物事を考えすぎないでください。バッチプロセスでdupeを検索するだけです。リソースの "アクセス"カウント統計がある可能性がありますだまされた候補者のうちのどれが放棄されたポストの結果であったかを見るために。

別のアプローチ:いくつかのログに対して着信POSTをスクリーニングして、それが繰り返しかどうかを確認します。見つけやすいはずです。リクエストのボディコンテンツがちょうどx時間前のリクエストのボディコンテンツと同じである場合は、繰り返しと見なしてください。そして、元のIP、同じ認証などの追加のパラメーターをチェックできます...

3
Marjan Venema

作成と更新のリクエストを1つのリクエスト(upsert)だけにまとめることもできると思います。新しいリソースを作成するために、クライアントPOST「ファクトリー」リソース。たとえば/ factory-url-nameにあります。その後、サーバーは新しいリソースのURIを返します。

2
freedev