RESTful APIでコマンドパターンを実装する


12

私はHTTP APIの設計を進めており、できればできるだけRESTfulにするようにしています。

機能がいくつかのリソースに広がるアクションがいくつかあり、いつか元に戻す必要があります。

これはコマンドパターンのように思えますが、どのようにリソースにモデル化できますか?

DepositActionのようなXXActionという名前の新しいリソースを紹介します。

POST /card/{card-id}/account/{account-id}/Deposit
AmountToDeposit=100, different parameters...

これにより、実際に新しいDepositActionが作成され、そのDo / Executeメソッドがアクティブになります。この場合、201 Created HTTPステータスを返すことは、アクションが正常に実行されたことを意味します。

後でクライアントができるアクションの詳細を確認したい場合

GET /action/{action-id}

Update / PUTはここでは関係ないため、ブロックする必要があります。

そして、アクションを元に戻すために、私は

DELETE /action/{action-id}

実際に関連オブジェクトのUndoメソッドを呼び出し、そのステータスを変更します。

やり直しが1回だけで満足だとしましょう。やり直す必要はありません。

このアプローチは大丈夫ですか?

落とし穴、それを使用しない理由はありますか?

これはクライアントのPOVから理解されていますか?


簡単に言えば、それはRESTではありません。
エヴァンプライス

3
@EvanPlaiceはそれについて詳しく説明しますか?それがまさに問題です。
ミシール

1
私は答えを詳しく述べたはずですが、ゲイリーの答えは、私が追加するもののほとんど/すべてをすでにカバーしています。URIはリソース(アクションではない)を表すことのみを想定しているため、休んでいないと言います。アクションは、GET / POST / PUT / DELETE / HEADを介して処理されます。RESTはOOPインターフェースと考えてください。目標は、APIを一般的なパターンに適合させ、可能な限り実装固有の詳細から切り離すことです。
エヴァンプライス

1
@EvanPlaiceわかりました、ありがとう。Depositは名詞および動詞と考えることができるため、ここで混乱していると思います
...-ミシール

この場合、URIは、借方記入(お金を受け取る)と貸方記入(お金を与える)がPOST要求を介して行われるアクションであるトランザクションを表す必要があります。お金がいずれかの方向に移動するたびに、作成される新しいトランザクションを表すため、両方にPOSTが使用されます。特定のケースでは、トランザクションはカード所有者のアカウントで行われているため、カードのアカウント番号はリソースURIです。
エヴァン

回答:


13

紛らわしい抽象化レイヤーを追加しています

APIは非常にクリーンでシンプルな状態から始まります。HTTP POSTは、指定されたパラメーターで新しいDepositリソースを作成します。次に、APIのコア部分ではなく、実装の詳細である「アクション」の概念を導入することにより、レールから脱出します。

別の方法として、このHTTP会話を検討してください...

POST / card / {card-id} / account / {account-id} / Deposit

AmountToDeposit = 100、さまざまなパラメーター...

201作成済み

Location = / card / 123 / account / 456 / Deposit / 789

次に、この操作を元に戻します(技術的には、これはバランスの取れた会計システムでは許可されませんが、何が許可されますか)。

DELETE / card / 123 / account / 456 / Deposit / 789

204コンテンツなし

APIコンシューマーは、Depositリソースを処理していることを知っており、(通常はHTTPのOPTIONSを介して)そのリソースで許可される操作を決定できます。

削除操作の実装は今日「アクション」を通じて行われますが、このシステムをたとえばC#からHaskellに移行し、「アクション」の二次概念が価値を付加し続けるというフロントエンドを維持する保証はありません。 、預金の主な概念は確かにそうです。

DELETEおよびDepositの代替をカバーするために編集する

削除操作を回避しながら、預金を効果的に削除するには、以下を実行する必要があります(一般的なトランザクションを使用して、預金と引き出しを許可します)。

POST / card / {card-id} / account / {account-id} / Transaction

金額= -100、異なるパラメータ...

201作成済み

Location = / card / 123 / account / 456 / Transation / 790

まったく反対の量(-100)を持つ新しいトランザクションリソースが作成されます。これにより、アカウントのバランスが0に戻され、元のトランザクションが無効になります。

次のような「ユーティリティ」エンドポイントの作成を検討できます。

POST / card / {card-id} / account / {account-id} / Transaction / 789 / Undo <-BAD!

同じ効果を得るために。ただし、これは、動詞を導入することにより、URIの識別子としてのセマンティクスを破壊します。識別子の名詞に固執し、HTTP動詞に制約された操作を維持することをお勧めします。これにより、識別子からパーマリンクを簡単に作成し、GETなどに使用できます。


3
+1「技術的には、これはバランスのとれた会計システムでは許可されません」。誰かが豆を数える方法を知っています。その声明は絶対に正しいので、元に戻す方法は、資金を差し戻す別のトランザクションを作成することです。総勘定元帳のエントリは、トランザクションが完了した後は常に不変で永続的であると見なされる必要があります。
エヴァンプライス

だから、質問で、/ action / ...を削除する代わりに/ deposit / ...を削除することは大丈夫ですか?
ミシール

2
@Mithir会計規則について説明していました。標準の複式簿記システムでは、トランザクションを削除することはありません。かつてコミットされた歴史は、人々を正直に保つために不変であると考えられています。あなたの場合でも、DELETEアクションを使用できますが、バックエンド(総勘定元帳データベーステーブルなど)で、ユーザーに返金(つまり、返金)を表す別のトランザクションを追加します。私はビーンカウンター(会計士)ではありませんが、「会計の原則I」コースで教えられている標準的なプラクティスの1つです。
エヴァンプライス

2
(続き)データベースログは、同様の方法でトランザクションを使用します。そのため、ログのみを使用してデータセットを複製および/または再構築することができます。トランザクションが時系列に再生される限り、その履歴の任意の時点からデータセットを再構築できるはずです。方程式から可変性を削除すると、一貫性が保証されます。
エヴァンプライス

1
トランザクションに名前を変更するだけで十分です。
ゲイリーロウ

1

RESTが存在する主な理由は、ネットワークエラーに対する回復力です。そのためには、すべての操作がべき等でなければなりません。

基本的なアプローチは理にかなっているように見えますが、DepositAction作成を記述する方法はthe 等であるとは思えません。これは修正する必要があります。重複したリクエストを検出するために使用される一意のID をクライアントに提供させます。したがって、作成は次のように変わります。

PUT /card/{card-id}/account/{account-id}/Deposit/{action-id}
AmountToDeposit=100, different parameters...

以前と同じコンテンツを使用して同じURLへの別のPUTが作成された201 created場合、コンテンツが同じ場合は応答が返され、コンテンツが異なる場合はエラーが返されます。これにより、クライアントは要求または応答が失われたかどうかを判断できないため、失敗したときに要求を単純に再送信できます。

PUTはリソースを書き込むだけでand等であるため、PUTを使用する方が理にかなっていますが、POSTを使用しても問題は発生しません。

トランザクションの詳細を見るために、クライアントはGET同じURL、つまり

GET /card/{card-id}/account/{account-id}/Deposit/{action-id}

元に戻すには、削除できます。しかし、サンプルが示すように実際にお金と関係がある場合は、説明責任(作成およびキャンセルされたトランザクションの痕跡が残っていること)のためではなく、「キャンセル」フラグを追加してPUTすることをお勧めします。

ここで、一意のIDを作成する方法を選択する必要があります。いくつかのオプションがあります:

  1. 含める必要がある交換の早い段階でクライアント固有のプレフィックスを発行します。
  2. 特別なPOSTリクエストを追加して、サーバーから空白の一意のIDを取得します。未使用のIDが実際に問題を引き起こすことはないため、この要求はdem等である必要はありません(実際にはできない)。
  3. 単にUUIDを使用します。誰もがそれらを使用しており、MACベースのものもランダムなものにも問題はないようです。

2
私の知る限り、POSTはi等ではありません。en.wikipedia.org/wiki/POST_(HTTP)#Affecting_server_state
ミシール

@Mithir:POSTはべき等であるとは見なされません。それでも可能です。しかし、すべてのREST操作はべき等であると想定されているため、POSTには基本的にRESTの場所がありません。
1月Hudec

1
私は混乱しています...私が読んだコンテンツと私がよく知っている既存の実装(ServiceStack、ASP.NET Web API)は、すべてPOSTがRESTにあることを示唆しています。
ミシール

3
RESTでは、べき等性はプロトコルまたはその応答コードではなく、リソースに割り当てられます。したがって、REST over HTTPでは、GET、PUT、DELETE、PATCHなどのメソッドはべき等と見なされますが、応答コードは後続の呼び出しで異なる場合があります。POSTは、すべての呼び出しが新しいリソースを作成するという意味でi等です。フィールディングのPOSTを使用しても構いません
ゲイリーロウ

1
べき等でない操作は、休息中に許可されます。その主張は完全に間違っています。
アンディ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.