5

ROA(リソース指向アーキテクチャ)を利用したRESTfulWebサービスを設計しています。

サーバーがリソースキーを指定した場合に、新しいリソースを作成するPUT要求のべき等性を保証する効率的な方法を見つけようとしています。

私の理解では、従来のアプローチは/CREATE_PERSONなどのタイプのトランザクションリソースを作成することです。新しい個人リソースを作成するためのクライアント/サーバーの相互作用は、次の2つの部分になります。

ステップ1:新しいPERSONリソースを作成するための一意のトランザクションIDを取得します:::

**Client request:**
POST /CREATE_PERSON

**Server response:**
200 OK
transaction-id:"as8yfasiob"

ステップ2:トランザクションIDを使用して一意であることが保証されているリクエストに新しい個人リソースを作成します:::

**Client request**
PUT /CREATE_PERSON/{transaction_id}
first_name="Big bubba"

**Server response**
201 Created             // (If the request is a duplicate, it would send this
PersonKey="398u4nsdf"   // same response without creating a new resource.  It
                        // would perhaps send an error response if the was used
                        // on a transaction id non-duplicate request, but I have
                        // control over the client, so I can guarantee that this
                        // won't happen)

このアプローチで私が目にする問題は、新しいPERSONリソースを作成する単一の操作を実行するために、サーバーに2つの要求を送信する必要があることです。これによりパフォーマンスの問題が発生し、ユーザーがクライアントがリクエストを完了するのを待つ可能性が高くなります。

リクエストごとにtransaction-idを事前送信するなど、最初のステップを排除するためのアイデアをハッシュ化しようとしていますが、私のアイデアのほとんどには他の問題があるか、アプリケーションのステートレス性を犠牲にする必要があります。

これを行う方法はありますか?

編集::::::

最終的に解決したのは、クライアントがUUIDを取得し、それをリクエストと一緒に送信することでした。UUIDは、16バイト(2 ^ 128)のスペースを占める非常に大きな数です。プログラミングマインドを持っている人が直感的に考えるかもしれないこととは反対に、UUIDをランダムに生成し、それが一意の値であると想定することは一般的に受け入れられています。これは、可能な値の数が非常に多いため、同じ数の2つをランダムに生成する確率が十分に低く、事実上不可能であるためです。

注意点の1つは、クライアントがサーバーにUUIDを要求するようにしていることです(GET uuid/)。これは、クライアントが実行されている環境を保証できないためです。クライアントに乱数ジェネレーターをシードするなどの問題が発生した場合は、UUIDの衝突が発生する可能性があります。

4

4 に答える 4

4

作成操作に間違った HTTP 動詞を使用しています。RFC 2616POSTは、およびの操作のセマンティックを指定しますPUT

パラグラフ 9.5:

POSTメソッドは、オリジン サーバーがリクエストに含まれるエンティティを、リクエスト ラインのリクエスト URI によって識別されるリソースの新しい従属として受け入れるようにリクエストするために使用されます。

パラグラフ 9.6

PUTメソッドは、同封されたエンティティが提供された Request-URI の下に格納されることを要求します。

その動作には微妙な詳細があります。たとえばPUT、指定された URL に新しいリソースが存在しない場合に作成するために使用できます。ただし、POST新しいエンティティをリクエスト URL に配置してはならず、PUT常にリクエスト URL に新しいエンティティを配置する必要があります。このリクエスト URL との関係は、POSTasCREATEおよびPUTas を定義しますUPDATE

そのセマンティックに従って、 を使用PUTして新しい人物を作成する場合は、 で作成する必要があります/CREATE_PERSON/{transaction_id}。つまり、最初のリクエストで返されるトランザクション ID は、後でそのレコードを取得するために使用される個人キーである必要があります。そのレコードの最終的な場所にならない URL に対してリクエストを行うべきではありません。PUT

POSTさらに良いことに、 toを使用してアトミック操作としてこれを行うことができます/CREATE_PERSON。これにより、単一のリクエストで新しい個人レコードを作成し、レスポンスで新しい ID を取得できます (これは HTTPLocationヘッダーでも参照する必要があります)。

一方、REST ガイドラインでは、動詞をリソース URL の一部に含めてはならないと規定しています。したがって、新しい人物を作成するための URL は、すべての人物のリストを取得する場所と同じでなければなりません/PERSONS(私は複数形を好みます :-))。

したがって、REST API は次のようになります。

  • すべての人を取得する -GET /PERSONS
  • 独身者を得る -GET /PERSONS/{id}
  • 新しい人を作成する -POST /PERSONS新しいレコードのデータを含む本文
  • 既存の人物を更新するか、既知の ID を持つ新しい人物を作成します -PUT /PERSONS/{id}更新されたレコードのデータを含む本文を使用します。
  • 既存の人を削除するには -DELETE /PERSONS/{id}

注: 個人的には、別のデータ セット (「貧乏人の外部キー」とも呼ばれる) から既存のレコードと同じ ID を持つサブ レコードを作成する必要がない限り、2 つの理由からレコードの作成に PUT を使用しないことを好みます。 -)))。

更新:冪等でPOSTはなく、それは HTTP 仕様によるものです。常に新しいリソースを返しますPOST。上記の例では、その新しいリソースがトランザクション コンテキストになります。

ただし、私が言いたいのは、PUTを使用して新しいリソース (個人レコード) を作成し、HTTP 仕様に従って、その新しいリソース自体を URL に配置する必要があるということです。特に、アプローチが壊れているのは、で使用する URL がPUT、新しいリソース自体の表現ではなく、POST によって作成されたトランザクション コンテキストの表現であることです。つまり、個人レコードはトランザクション レコードの更新の副作用であり、トランザクション レコードの直接の結果 (更新されたトランザクション レコード) ではありません。

もちろん、このアプローチではPUT、個人レコードが作成されてトランザクションが「ファイナライズ」されると、後続のPUTリクエストは何もしないため、リクエストは冪等になります。PUTしかし、今度は別の問題があります。実際に個人レコードを更新するには、別の URL にリクエストを送信する必要があります。これは、個人レコードが作成されたトランザクションではなく、個人レコードを表す URL です。これで、同じリソースを操作するために、API クライアントが認識してリクエストを作成する必要がある 2 つの別個の URL ができました。

または、トランザクション レコードにコピーされた最後のリソースの状態を完全に表現し、個人レコードの更新もトランザクション URL を介して更新することができます。ただし、この時点では、トランザクション URL個人レコードを意図して作成されたものでありPOST、最初にリクエストによって作成されたことを意味します。

于 2010-06-02T03:56:28.593 に答える
2

この投稿に出くわしました: GUID が一意ではないという単純な証明

この質問は一般的に嘲笑されますが、回答の一部は GUID のより深い説明に入ります。GUID はサイズが 2^128 の数値であり、このサイズの同じ数値を 2 つランダムに生成する確率は非常に低いため、すべての実用的な目的では不可能なようです。

おそらく、クライアントは、サーバーに照会する代わりに、GUID のサイズの独自のトランザクション ID を生成することができます。誰かがこれを信用できない場合は、私に知らせてください。

于 2010-06-05T01:39:40.623 に答える
1

あなたの質問に直接答えられるかどうかはわかりませんが、答えにつながる可能性のある問題がいくつかあります。

最初の操作は GET ですが、新しいトランザクション ID を「作成」しているため、安全な操作ではありません。POST を使用するのがより適切な動詞であることをお勧めします。

あなたは、2 回の往復によってユーザーが認識するパフォーマンスの問題について懸念していると述べています。これは、ユーザーが一度に 500 個のオブジェクトを作成しようとしているからですか、それともネットワーク上で大きな遅延の問題が発生しているからですか?

ユーザーの要求に応じてオブジェクトを作成するのに 2 回の往復が妥当な費用ではない場合、HTTP はシナリオに適したプロトコルではないことをお勧めします。ただし、ユーザーが一度に大量のオブジェクトを作成する必要がある場合は、それを可能にするリソースを公開するより良い方法を見つけることができます。

于 2010-06-02T03:42:28.267 に答える
0

最初の呼び出しのペイロードも含めて、単純な POST を使用してみませんか。このようにして、余分な呼び出しを節約し、トランザクションを生成する必要はありません。


POST /persons

first_name=foo

応答は次のようになります。


HTTP 201 CREATED
...
payload_containing_data_and_auto_generated_id

サーバー内部で ID が生成されます。簡単にするために、人工的な主キーを使用します (たとえば、データベースからの自動インクリメント ID)。

于 2010-06-02T20:16:06.303 に答える