157

次のユースケースを REST でどのように実装するのか疑問に思っています。概念モデルを損なうことなく、それを行うことさえ可能ですか?

1 つのトランザクションの範囲内で複数のリソースを読み取りまたは更新します。たとえば、ボブの銀行口座からジョンの口座に $100 を送金します。

私の知る限り、これを実装する唯一の方法は不正行為です。John または Bob に関連付けられたリソースに POST し、単一のトランザクションを使用して操作全体を実行できます。私の知る限り、これは REST アーキテクチャを壊します。実際に個々のリソースを操作するのではなく、基本的に POST を介して RPC 呼び出しをトンネリングしているためです。

4

13 に答える 13

93

RESTful ショッピング バスケットのシナリオを考えてみましょう。ショッピング バスケットは、概念的にはトランザクション ラッパーです。複数のアイテムをショッピング バスケットに追加し、そのバスケットを送信して注文を処理できるのと同じ方法で、ボブのアカウント エントリをトランザクション ラッパーに追加し、次にビルのアカウント エントリをラッパーに追加できます。すべての部品が配置されたら、すべてのコンポーネント部品を含むトランザクション ラッパーを POST/PUT できます。

于 2008-09-29T02:13:30.790 に答える
63

この質問では答えられない重要なケースがいくつかありますが、Google での検索用語のランキングが高いため、これは残念だと思います :-)

具体的には、次のような適切な方法があります: POST を 2 回行った場合 (中間でキャッシュが中断されたため)、金額を 2 回転送するべきではありません。

これを実現するには、トランザクションをオブジェクトとして作成します。これには、既知のすべてのデータが含まれ、トランザクションが保留状態になる可能性があります。

POST /transfer/txn
{"source":"john's account", "destination":"bob's account", "amount":10}

{"id":"/transfer/txn/12345", "state":"pending", "source":...}

このトランザクションを取得したら、次のようにコミットできます。

PUT /transfer/txn/12345
{"id":"/transfer/txn/12345", "state":"committed", ...}

{"id":"/transfer/txn/12345", "state":"committed", ...}

この時点では、複数のプットは問題ではないことに注意してください。txn の GET でさえ、現在の状態を返します。具体的には、2 番目の PUT は、最初の PUT が既に適切な状態にあることを検出し、単にそれを返します。または、既に「コミット」状態になった後に「ロールバック」状態にしようとすると、エラー、および実際にコミットされたトランザクションが返されます。

単一のデータベース、またはトランザクション モニターが統合されたデータベースと対話する限り、このメカニズムは実際には問題なく機能します。必要に応じて Expires ヘッダーを使用して表現することもできる、トランザクションのタイムアウトを追加で導入することもできます。

于 2011-01-19T22:46:28.447 に答える
35

REST用語では、リソースはCRUD(作成/読み取り/更新/削除)動詞で動作できる名詞です。「送金」動詞がないため、CRUDで処理できる「トランザクション」リソースを定義する必要があります。これがHTTP+POXの例です。最初のステップは、新しい空のトランザクションを作成(HTTP POSTメソッド)することです。

POST /transaction

これにより、トランザクションIDが返されます(例:「1234」およびそれに応じたURL「/ transaction / 1234」)。このPOSTを複数回起動しても、複数のIDで同じトランザクションが作成されることはなく、「保留中」の状態の導入も回避されることに注意してください。また、POSTは常にべき等であるとは限らないため(REST要件)、POSTのデータを最小限に抑えることをお勧めします。

トランザクションIDの生成はクライアントに任せることができます。この場合、POST / transaction / 1234を使用してトランザクション「1234」を作成すると、サーバーがすでに存在する場合はエラーを返します。エラー応答で、サーバーは現在使用されていないIDと適切なURLを返す可能性があります。GETメソッドを使用してサーバーに新しいIDを照会することはお勧めできません。これは、GETがサーバーの状態を変更することはなく、新しいIDを作成/予約するとサーバーの状態が変更されるためです。

次に、すべてのデータを使用してトランザクションをUPDATE (PUT HTTPメソッド)し、暗黙的にコミットします。

PUT /transaction/1234
<transaction>
  <from>/account/john</from>
  <to>/account/bob</to>
  <amount>100</amount>
</transaction>

ID「1234」のトランザクションが以前にPUTされた場合、サーバーはエラー応答を返します。それ以外の場合は、OK応答と、完了したトランザクションを表示するためのURLを返します。

注意:/ account / johnでは、「john」は実際にはJohnの一意のアカウント番号である必要があります。

于 2011-04-03T23:56:16.000 に答える
20

すばらしい質問です。RESTは主にデータベースのような例で説明されており、何かが保存、更新、取得、削除されます。サーバーが何らかの方法でデータを処理することになっているこのような例はほとんどありません。結局、ロイ・フィールディングはhttpに基づいた彼の論文に何も含まれていなかったと思います。

しかし、彼は、リンクが次の状態に移動する、ステートマシンとしての「RepresentationalStateTransfer」について語っています。このようにして、ドキュメント(表現)は、サーバーが実行するのではなく、クライアントの状態を追跡します。このように、クライアントの状態はなく、現在のリンクに関する状態のみがあります。

私はこれについて考えていましたが、サーバーに何かを処理させるために、アップロードすると、サーバーが自動的に関連リソースを作成し、それらへのリンクを提供するのは合理的だと思います(実際には、それらを自動的に作成する必要はありません:それはあなたにリンクを伝えるだけであり、あなたがそれらをたどるときとあなたがそれらをたどる場合にのみそれらを作成します-怠惰な作成)。また、新しい関連リソースを作成するためのリンクを提供するために、関連リソースは同じURIを持ちますが、より長くなります(接尾辞が追加されます)。例えば:

  1. すべての情報を含むトランザクションの概念の表現をアップロード(POST )します。 これはRPC呼び出しのように見えますが、実際には「提案されたトランザクションリソース」を作成しています。例:URI: グリッチにより、それぞれが異なるURIを持つ複数のそのようなリソースが作成されます。/transaction
  2. サーバーの応答には、作成されたリソースのURIとその表現が示されます。これには、新しい「コミットされたトランザクションリソース」の関連リソースを作成するためのリンク( URI )が含まれます。その他の関連リソースは、提案されたトランザクションを削除するためのリンクです。これらは、クライアントが追跡できるステートマシンの状態です。論理的には、これらは、クライアントが提供した情報を超えて、サーバー上に作成されたリソースの一部です。例:URI :、 /transaction/1234/proposed/transaction/1234/committed
  3. リンクにPOSTして、「コミットされたトランザクションリソース」を作成します。これにより、そのリソースが作成され、サーバーの状態(2つのアカウントの残高)が変更されます**。その性質上、このリソースは1回だけ作成でき、更新することはできません。したがって、多くのトランザクションをコミットするグリッチは発生しません。
  4. これらの2つのリソースを取得して、それらの状態を確認できます。POSTが他のリソースを変更できると仮定すると、プロポーザルは「コミット済み」としてフラグが立てられます(または、おそらくまったく利用できません)。

これは、Webページの動作に似ており、最終的なWebページには「これを実行してもよろしいですか?」と表示されます。その最終的なWebページは、それ自体がトランザクションの状態を表したものであり、次の状態に移動するためのリンクが含まれています。金融取引だけではありません。また、(例)ウィキペディアでプレビューしてからコミットします。RESTの違いは、一連の状態の各ステージに明示的な名前(URI)があることだと思います。

実際の取引/販売では、取引のさまざまな段階(提案、発注書、領収書など)ごとにさまざまな物理的な文書が存在することがよくあります。家を買うために、決済などでさらに。

OTOHこれは私にとってセマンティクスで遊んでいるような気がします。「動詞(RPC呼び出し)の代わりに名詞(URI)を使用するため」、動詞を名詞に変換してRESTfulにするという名詞化には不安があります。つまり、動詞「committhistransaction」ではなく名詞「committedtransactionresource」です。名詞化の利点の1つは、他の方法でリソースを指定する必要がなく、名前でリソースを参照できることです(セッション状態の維持など、「この」トランザクションが何であるかがわかります...)

しかし、重要な質問は次のとおりです。このアプローチの利点は何ですか?つまり、このRESTスタイルはRPCスタイルよりもどのように優れていますか?Webページに最適な手法は、保存/取得/更新/削除だけでなく、情報処理にも役立ちますか?RESTの主な利点はスケーラビリティだと思います。その1つの側面は、クライアントの状態を明示的に維持する必要はありません(ただし、リソースのURIで暗黙的にし、次の状態をその表現のリンクとして作成します)。その意味でそれは役に立ちます。おそらくこれはレイヤー/パイプラインにも役立ちますか?OTOHは1人のユーザーだけが特定のトランザクションを確認するため、他のユーザーが読み取れるようにキャッシュすることに利点はありません。これはhttpの大きなメリットです。

于 2011-12-14T04:33:37.473 に答える
15

私はこの話題から10年間離れていました。戻ってくると、rest+reliable をグーグルで検索するときに、科学を装った宗教が信じられません。混乱は神話です。

この大まかな質問を 3 つに分けます。

  • ダウンストリーム サービス。開発する Web サービスには、使用するダウンストリーム サービスがあり、そのトランザクション構文に従うしかありません。これらすべてをサービスのユーザーから隠し、操作のすべての部分がグループとして成功または失敗することを確認してから、この結果をユーザーに返す必要があります。
  • あなたのサービス。クライアントは、Web サービス呼び出しに対して明確な結果を望んでおり、実質的なリソースに対して直接 POST、PUT、または DELETE 要求を行う通常の REST パターンは、この確実性を提供する方法として貧弱であり、簡単に改善できると思います。信頼性を重視する場合は、アクション リクエストを特定する必要があります。この ID は、クライアント上で作成された GUID か、サーバー上のリレーショナル DB のシード値のどちらでもかまいません。サーバー生成 ID の場合、「プリフライト」要求応答を使用してアクションの ID を交換します。このリクエストが失敗するか半分成功しても問題ありません。クライアントはリクエストを繰り返すだけです。未使用の ID は問題ありません。

    これは、後続のすべてのリクエストが完全に冪等になるため、重要です。つまり、n回繰り返された場合、同じ結果が返され、それ以上何も起こらないという意味です。サーバーはアクション ID に対するすべての応答を保存し、同じ要求を確認すると、同じ応答を再生します。パターンの完全な処理は、この google docにあります。このドキュメントは、REST プリンシパルに広く従うと私が信じている (!) 実装を提案しています。専門家は、それが他の人をどのように侵害するかをきっと教えてくれるでしょう。このパターンは、ダウンストリーム トランザクションが含まれているかどうかに関係なく、Web サービスへの安全でない呼び出しに対して有効に使用できます。
  • アップストリーム サービスによって制御される「トランザクション」へのサービスの統合。Web サービスのコンテキストでは、完全な ACID トランザクションは通常、努力する価値がないと見なされますが、確認応答でキャンセルおよび/または確認リンクを提供することにより、サービスの消費者を大幅に支援し、補償によるトランザクションを実現できます。

あなたの要件は基本的なものです。あなたの解決策がコーシャではないと人々に言わせてはいけません. それらのアーキテクチャが問題にどれだけうまく、そしてどれほど簡単に対処しているかに照らして判断してください。

于 2016-02-15T09:35:39.710 に答える
10

独自の「トランザクションID」タイプのtx管理をロールバックする必要があります。したがって、4 つの呼び出しになります。

http://service/transaction (some sort of tx request)
http://service/bankaccount/bob (give tx id)
http://service/bankaccount/john (give tx id)
http://service/transaction (request to commit)

DB(負荷分散されている場合)またはメモリなどにアクションを保存してから、コミット、ロールバック、タイムアウトを処理する必要があります。

公園での休息日ではありません。

于 2008-09-29T01:52:04.630 に答える
3

まず第一に、送金は 1 回のリソース呼び出しではできないことではありません。あなたがしたい行動は送金です。したがって、送金リソースを送信者のアカウントに追加します。

POST: accounts/alice, new Transfer {target:"BOB", abmount:100, currency:"CHF"}.

終わり。これがアトミックでなければならないトランザクションであることなどを知る必要はありません。単に送金するだけです。AからBにお金を送る.


ただし、ここでのまれなケースの一般的な解決策は次のとおりです。

定義されたコンテキストで多くのリソースを含む非常に複雑なことを行いたい場合は、実際に何となぜの障壁 (ビジネスと実装の知識) を越える多くの制限がある場合は、状態を転送する必要があります。REST はステートレスである必要があるため、クライアントは状態を転送する必要があります。

状態を転送する場合は、内部の情報をクライアントから隠す必要があります。クライアントは、実装に必要なだけの内部情報を知っているべきではありませんが、ビジネスに関連する情報は持っていません。これらの情報にビジネス上の価値がない場合は、状態を暗号化し、トークン、パスなどのメタファーを使用する必要があります。

このようにして、内部状態を渡し、暗号化と署名を使用してシステムを安全かつ健全にすることができます。クライアントが状態情報をやり取りする理由に適した抽象化を見つけることは、設計とアーキテクチャ次第です。


本当の解決策:

REST は HTTP を話していることを思い出してください。HTTP には Cookie を使用するという概念があります。これらの Cookie は、人々が REST API やワークフロー、複数のリソースやリクエストにまたがるやり取りについて話すときに忘れられることがよくあります。

ウィキペディアに HTTP Cookie について書かれていることを思い出してください。

Cookie は、Web サイトがステートフルな情報 (ショッピング カート内のアイテムなど) を記憶したり、ユーザーのブラウジング アクティビティ (特定のボタンのクリック、ログイン、ユーザーがこれまでにアクセスしたページの記録など) を記録したりするための信頼できるメカニズムとなるように設計されています。数か月または数年前にさかのぼります)。

したがって、基本的に状態を渡す必要がある場合は、Cookie を使用します。まったく同じ理由で設計されています.HTTPであるため、設計上RESTと互換性があります:)。


より良い解決策:

複数のリクエストを含むワークフローを実行するクライアントについて話す場合、通常はプロトコルについて話します。すべての形式のプロトコルには、B を実行する前にステップ A を実行するなど、潜在的な各ステップの前提条件のセットが付属しています。

これは当然のことですが、プロトコルをクライアントに公開すると、すべてがより複雑になります。それを避けるためには、現実世界で複雑な相互作用や物事をしなければならないときに私たちが何をするかを考えてみてください... . エージェントを使用します。

エージェントのメタファを使用すると、必要なすべてのステップを実行できるリソースを提供し、実際の割り当て/指示をリストに保存できます (したがって、エージェントまたは「エージェンシー」で POST を使用できます)。

複雑な例:

家を買う:

あなたの信頼性を証明する必要があります (警察の記録を提供するなど)。財務の詳細を確認する必要があります。弁護士と資金を保管している信頼できる第三者を使用して実際の家を購入する必要があります。家が現在あなたのものであることを確認し、税務記録などに購入内容を追加します (例として、いくつかの手順が間違っているか、その他の手順である可能性があります)。

これらの手順は完了するまでに数日かかる場合があり、並行して実行できるものもあります。

これを行うには、次のようなタスク購入家をエージェントに与えるだけです。

POST: agency.com/ { task: "buy house", target:"link:toHouse", credibilities:"IamMe"}.

終わり。エージェンシーは、このジョブのステータスを確認および追跡するために使用できる参照をあなたに送り返します。残りは、エージェンシーのエージェントによって自動的に行われます。

たとえば、バグトラッカーについて考えてみてください。基本的に、バグを報告し、バグ ID を使用して何が起こっているかを確認できます。サービスを使用して、このリソースの変更をリッスンすることもできます。ミッション完了。

于 2015-10-19T09:22:09.030 に答える
2

REST でサーバー側のトランザクションを使用しないでください。

REST 制約の 1 つ:

ステートレス

クライアントとサーバーの通信は、リクエスト間でクライアント コンテキストがサーバーに保存されないため、さらに制限されます。クライアントからの各リクエストには、リクエストを処理するために必要なすべての情報が含まれており、セッション状態はすべてクライアントに保持されます。

唯一の RESTful な方法は、トランザクション REDO ログを作成し、それをクライアント状態にすることです。リクエストにより、クライアントはREDOログを送信し、サーバーはトランザクションをやり直し、

  1. トランザクションをロールバックしますが、新しいトランザクション REDO ログを提供します (さらに 1 ステップ)
  2. または最終的にトランザクションを完了します。

しかし、サーバー側のトランザクションをサポートするサーバー セッション ベースのテクノロジを使用する方が簡単かもしれません。

于 2013-07-05T08:58:17.080 に答える
1

単純なケース (分散リソースなし) では、トランザクションを作成する行為が最終目的を達成するリソースと考えることができます。

したがって、 と の間<url-base>/account/aで転送<url-base>/account/bするには、次を に投稿できます<url-base>/transfer

<転送>
    <from><url-base>/account/a</from>
    <to><url-base>/account/b</to>
    <金額>50</金額>
</転送>

これにより、新しい転送リソースが作成され、転送の新しい URL が返されます<url-base>/transfer/256

投稿が成功した時点で、「実際の」トランザクションがサーバー上で実行され、金額が 1 つのアカウントから削除され、別のアカウントに追加されます。

ただし、これは分散トランザクションをカバーしていません (たとえば、'a' が 1 つのサービスの背後にある銀行で保持され、'b' が別のサービスの背後にある別の銀行で保持されている場合)。分散トランザクションを必要としない方法での操作」.

于 2010-10-08T03:37:33.737 に答える
1

この場合、この状況で REST の純粋な理論を破ることはまったく問題ないと思います。いずれにせよ、依存オブジェクトを必要とするビジネスケースで依存オブジェクトに触れてはならないということは、REST には実際にはないと思います。

データベースを活用してカスタム トランザクション マネージャーを作成できるのに、余分な手間をかけてカスタム トランザクション マネージャーを作成する価値はないと本当に思います。

于 2008-09-29T02:35:37.257 に答える
-4

URL/リソースにTANを含めることができると思います:

  1. ID を取得するための PUT /transaction (例: "1")
  2. [PUT、GET、POST、なんでも] /1/account/bob
  3. [PUT、GET、POST、なんでも] /1/account/bill
  4. ID 1 の DELETE /トランザクション

ただのアイデア。

于 2008-09-29T02:01:48.117 に答える