質問

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

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

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

役に立ちましたか?

解決

RESTfulショッピングバスケットシナリオを検討してください。買い物かごは、概念的にはトランザクションラッパーです。ショッピングバスケットに複数のアイテムを追加し、そのバスケットを送信して注文を処理できるのと同じ方法で、Bobのアカウントエントリをトランザクションラッパーに追加し、Billのアカウントエントリをラッパーに追加できます。すべてのピースが揃ったら、すべてのコンポーネントピースでトランザクションラッパーをPOST / PUTできます。

他のヒント

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

具体的には、適切な方法は次のとおりです。2回POSTを実行する場合(中間キャッシュが一時的に中断されるため)、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", ...}

この時点では、複数のputは重要ではありません。 txnに対するGETでさえ、現在の状態を返します。具体的には、2番目のPUTは、最初のPUTがすでに適切な状態にあることを検出し、単にそれを返します-または、<!> quot; rolledback <!> quot;に入れようとした場合すでに<!> quot; committed <!> quotになっている状態。状態では、エラーが発生し、実際にコミットされたトランザクションが返されます。

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

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

POST /transaction

これは、トランザクションIDを返します。 <!> quot; 1234 <!> quot;およびURL <!> quot; / transaction / 1234 <!> quot;に従って。このPOSTを複数回実行しても、複数のIDを持つ同じトランザクションは作成されず、<!> quot; pending <!> quot;の導入も回避されることに注意してください。状態。また、POSTは常にi等(REST要件)であるとは限らないため、POSTのデータを最小限に抑えることをお勧めします。

トランザクションIDの生成はクライアントに任せることができます。この場合、POST / transaction / 1234でトランザクション<!> quot; 1234 <!> quot;を作成します。また、サーバーが既に存在する場合はエラーを返します。エラー応答では、サーバーは現在使用されていない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 <!> quot; 1234 <!> quotのトランザクションの場合以前にPUTされていた場合、サーバーはエラー応答を返します。それ以外の場合、OK応答と完了したトランザクションを表示するURLを返します。

NB:/ account / john、<!> quot; john <!> quot;ジョンの一意のアカウント番号である必要があります。

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

しかし、彼は<!> quot; representational state transfer <!> quot;について話します。リンクが次の状態に移動する状態マシンとして。この方法では、ドキュメント(表現)がクライアントの状態を追跡します。サーバーが行う必要はありません。この方法では、クライアントの状態はなく、現在のリンクに関する状態のみが表示されます。

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

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

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

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

OTOHこれは、セマンティクスで遊んでいるような気がします。動詞(RPC呼び出し)<!> quot;の代わりに名詞(URI)を使用するため、動詞を名詞に変換してRESTfulにする<!> quot;の名義化には不快です。すなわち、名詞<!> quot; committed transaction resource <!> quot;動詞<!> quot; commit this transaction <!> quot;の代わりに。名義化の利点の1つは、リソースを他の方法で指定する代わりに、名前でリソースを参照できることです(セッション状態の維持など、<!> quot; this <!> quot;トランザクションis ...)

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

ここで議論を要約するために立ち止まる場合、RESTは多くのAPIに適切ではないことは明らかです。クライアントとサーバーの両方に対して提案されたすべてのフープを飛び越えて、問題に合わないいくつかの原則を順守するのはなぜですか?より良い原則は、クライアントにアプリケーションで作曲する最も簡単で自然で生産的な方法を提供することです。

要約すると、アプリケーションで本当に多くのトランザクション(インスタンスではなくタイプ)を実行している場合、RESTful APIを作成するべきではありません。

独自の<!> quot; transaction id <!> quot;をロールする必要があります。 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(負荷分散の場合)またはメモリなどにアクションを保存し、コミット、ロールバック、タイムアウトを処理する必要があります。

公園でのRESTfulな日ではありません。

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

この広範な質問を3つに分けます:

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

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

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

まず、送金は1回のリソース呼び出しで行うことはできません。あなたがしたいアクションはお金を送ることです。そのため、送金者のアカウントに送金リソースを追加します。

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

完了。これはアトミックなトランザクションでなければならないことを知る必要はありません。 AからBに送金します。


ただし、ここでは一般的な解決策としてまれなケースがあります:

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

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

この方法で内部状態を渡すことができ、暗号化とシステムへの署名を使用しても安全で健全です。クライアントが状態情報を渡す理由をクライアントに適切に抽象化することは、設計とアーキテクチャ次第です。


実際のソリューション:

RESTはHTTPについて話していることを忘れないでください。これらのCookieは、複数のリソースまたはリクエストにまたがるREST APIおよびワークフローと対話について人々が話すときに忘れられることがよくあります。

WikipediaのHTTP Cookieに関する記述を思い出してください:

  

Cookieは、ウェブサイトがステートフル情報(ショッピングカート内のアイテムなど)を記憶したり、ユーザーのブラウジングアクティビティ(特定のボタンのクリック、ログイン、訪問したページの記録など)を記録したり、ユーザーは数か月前または数年前まで遡ります)。

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


より良い解決策:

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

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

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

複雑な例:

家を買う:

信頼性を証明する必要があります(警察の記録エントリを提供するなど)、財務の詳細を確認する必要があります、弁護士と資金を保管する信頼できる第三者を使用して実際の家を購入する必要があり、家が現在属していることを確認するを購入し、購入記録を税務記録などに追加します(例として、一部の手順が間違っているなど)。

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

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

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

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

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

この場合、この状況でRESTの純粋な理論を破ることは完全に受け入れられると思います。いずれにせよ、RESTには、それを必要とするビジネスケースで依存オブジェクトに触れることができないと言っているものは実際にはないと思います。

データベースを活用してカスタムトランザクションマネージャーを作成するだけで、カスタムトランザクションマネージャーを作成するために必要な余分な作業を行う価値はないと思います。

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

REST制約の1つ:

  

ステートレス

     

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

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

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

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

クライアントで生成された一意の識別子を使用して、接続の中断がAPIによって保存された重複を暗示しないことを保証する場合だと思います。

クライアントが生成したGUIDフィールドを転送オブジェクトとともに使用し、同じGUIDが再度挿入されないようにすることは、銀行振込の問題に対するより簡単なソリューションになると思います。

複数の航空券の予約やマイクロアーキテクチャなど、より複雑なシナリオについては知らない。

取引の経験に関連する主題に関する論文を見つけましたRESTfulサービスのトランザクションアトミック性

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

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

<transfer>
    <from><url-base>/account/a</from>
    <to><url-base>/account/b</to>
    <amount>50</amount>
</transfer>

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

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

ただし、これは分散トランザクションをカバーしません(たとえば、「a」が1つのサービスの背後の1つの銀行で保持され、「b」が別のサービスの背後の別の銀行で保持される場合)-<! > quot;すべての操作を、分散トランザクションを必要としない方法で表現してください<!> quot;。

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

  1. PUT / transactionでIDを取得します(例:<!> quot; 1 <!> quot;)
  2. [PUT、GET、POST、何でも] / 1 / account / bob
  3. [PUT、GET、POST、何でも] / 1 / account / bill
  4. ID 1のDELETE / transaction

ただのアイデア。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top