web-dev-qa-db-ja.com

RESTfulな方法でリソースのサーバー側メソッドを呼び出す

RESTの基本的な理解を持っていることに注意してください。このURLがあるとします。

http://api.animals.com/v1/dogs/1/

そして今、サーバーに犬の鳴き声を作らせたいです。サーバーのみがこれを行う方法を知っています。永遠の残りの間、犬を10分ごとにbarえるCRONジョブで実行させたいとしましょう。その呼び出しはどのように見えますか?私はこれをしたいのです:

URLリクエスト:

ACTION http://api.animals.com/v1/dogs/1/

リクエスト本文で:

{"action":"bark"}

独自のHTTPメソッドを作成することに腹を立てる前に、RESTfulな方法でサーバー側のメソッドを呼び出す方法について、私を助けてより良いアイデアを教えてください。 :)

説明の編集

「樹皮」メソッドが何をするかについてのより明確な説明。以下に、異なる構造のAPI呼び出しをもたらす可能性のあるいくつかのオプションを示します。

  1. barkは単にdog.emailにメールを送信し、何も記録しません。
  2. barkは、dog.emailにメールを送信し、dog.barkCountを1増やします。
  3. 樹皮は、樹皮が発生したときにbark.timestamp記録で新しい「樹皮」レコードを作成します。また、dog.barkCountを1増やします。
  4. barkはシステムコマンドを実行して、Githubから最新バージョンのドッグコードを取得します。次に、新しいdogコードが生産中であることを伝えるテキストメッセージをdog.ownerに送信します。
132
Kirk Ouimet

I 以前に答えた 、しかし、この答えは私の古い答えと矛盾し、解決策を得るための非常に異なる戦略に従います。 RESTおよびHTTPを定義する概念からHTTP要求がどのように構築されるか。また、PATCHまたはPOSTの代わりにPUTを使用します。

REST制約、次にHTTPのコンポーネント、次に可能な解決策を通過します。

REST

RESTは、スケーラブルにするために分散ハイパーメディアシステムに適用されることを目的とした一連の制約です。アクションをリモートで制御するという文脈でそれを理解するには、アクションをリモートで制御することを、分散ハイパーメディアシステムの一部、つまり相互接続された情報を発見、表示、および変更するシステムの一部として考える必要があります。それが価値があるよりも厄介な場合は、おそらくRESTfulにしようとするのは良くありません。ポート80を介してサーバー上でアクションをトリガーできる「コントロールパネル」タイプのGUIが必要な場合は、おそらくHTTP要求/応答またはWebSocketを介したJSON-RPCのような単純なRPCインターフェイスが必要です。

しかし、RESTは魅​​力的な考え方であり、質問の例はたまたまRESTfulインターフェースを使用して簡単にモデル化できるので、楽しみと教育のために挑戦しましょう。

RESTは、4つのインターフェイス制約により 定義 です。

リソースの識別。表現によるリソースの操作。自己記述的なメッセージ。そして、アプリケーション状態のエンジンとしてのハイパーメディア。

これらの制約を満たし、あるコンピューターが別のコンピューターに犬の鳴き声を出すように指示するインターフェースを定義する方法を尋ねます。具体的には、インターフェイスをHTTPにしたいので、意図したとおりに使用したときにHTTP RESTfulになる機能を破棄したくありません。

最初の制約から始めましょう:resource identification

名前を付けることができる情報は、ドキュメントや画像、一時的なサービス(「ロサンゼルスの今日の天気」など)、その他のリソースのコレクション、非仮想オブジェクト(人など)などのリソースです。 。

犬は資源です。特定する必要があります。

より正確には、リソース R は時間的に変化するメンバーシップ関数です MRt)、時間の t 同等のエンティティまたは値のセットにマップします。セット内の値は、リソース表現および/またはリソース識別子です。

識別子と表現のセットを取得し、それらがすべて特定の時間に相互に関連付けられていると言うことで、犬をmodelします。ここでは、識別子「dog#1」を使用してみましょう。これにより、2番目と3番目の制約に到達します:resource representationおよびself-description

RESTコンポーネントは、リプレゼンテーションを使用してリソースの現在の状態または目的の状態をキャプチャし、コンポーネント間でそのリプレゼンテーションを転送することにより、リソースに対してアクションを実行します。表現は、一連のバイトと、それらのバイトを記述する表現メタデータです。

以下は、犬の意図された状態、つまり識別子「dog#1」に関連付けたい表現をキャプチャする一連のバイトです(犬の名前、健康を考慮しないため、状態の一部のみを表すことに注意してください) 、または過去の樹皮さえ):

この状態の変更が行われてから10分ごとにbarえ続けており、無期限に継続します。

それを記述するメタデータに添付されることになっています。このメタデータは役に立つかもしれません:

英語の声明です。意図した状態の一部を説明します。複数回受信した場合は、最初のもののみが効果を持つようにします。

最後に、4番目の制約を見てみましょう:HATEOAS

REST ...アプリケーションを情報のまとまりのある構造とみなし、ユーザーが目的のタスクを実行できる代替手段を制御します。たとえば、オンライン辞書でWordを検索することは、仮想博物館を巡回することや、試験のために勉強するために一連のノートを確認することと同様に、1つのアプリケーションです。 ...アプリケーションの次の制御状態は、最初に要求されたリソースの表現に存在するため、その最初の表現を取得することが優先事項です。 ...したがって、モデルアプリケーションは、現在の表現セット内の代替状態遷移を調べて選択することにより、ある状態から次の状態に移動するエンジンです。

RESTfulインターフェースでは、クライアントは表現を受信または送信する方法を把握するためにリソース表現を受信します。クライアントが表現のチェーンをたどってその情報に到達したとしても、クライアントが受信または送信できるすべての表現を受信または送信する方法を把握できるアプリケーションのどこかに表現がなければなりません。これは十分に単純なようです:

クライアントは、ホームページとして識別されるリソースの表現を要求します。応答として、クライアントが必要とする可能性のあるすべての犬の識別子を含む表現を取得します。クライアントはそこから識別子を抽出し、識別された犬とどのようにやり取りできるかをサービスに尋ねます。サービスは、クライアントが犬の意図した状態の一部を説明する英語の声明を送信できると言います。次に、クライアントはそのようなステートメントを送信し、成功メッセージまたはエラーメッセージを受信します。

HTTP

HTTPは、次のようにREST制約を実装します。

リソース識別:URI

リソース表現:エンティティ本体

self-description:メソッドまたはステータスコード、ヘッダー、および場合によってはエンティティ本体の一部(XMLスキーマのURIなど)

HATEOAS:ハイパーリンク

URIとしてhttp://api.animals.com/v1/dogs/1を決定しました。クライアントがサイトのあるページからこれを取得したと仮定しましょう。

このエンティティ本体を使用してみましょう(nextの値はタイムスタンプです。0の値は「このリクエストを受信したとき」を意味します):

{"barks": {"next": 0, "frequency": 10}}

ここでメソッドが必要です。 PATCH は、私たちが決定した「意図した状態の一部」の説明に適合します。

PATCHメソッドは、リクエストエンティティに記述された一連の変更を、リクエストURIで識別されるリソースに適用することをリクエストします。

そしていくつかのヘッダー:

エンティティ本体の言語を示すには:Content-Type: application/json

1回だけ発生するようにするには:If-Unmodified-Since: <date/time this was first sent>

そして、リクエストがあります:

PATCH /v1/dogs/1/ HTTP/1.1
Host: api.animals.com
Content-Type: application/json
If-Unmodified-Since: <date/time this was first sent>
[other headers]

{"barks": {"next": 0, "frequency": 10}}

成功すると、クライアントは応答で 204 ステータスコードを受信するか、205の表現が新しいnewえスケジュールを反映するように変更された場合は/v1/dogs/1/を受信する必要があります。

失敗すると、403とその理由を示す有用なメッセージを受け取ります。

RESTは、GET /v1/dogs/1/に応じて表現で樹皮のスケジュールを反映することは必須ではありませんが、JSON表現にこれが含まれていれば最も意味があります。

"barks": {
    "previous": [x_1, x_2, ..., x_n],
    "next": x_n,
    "frequency": 10
}

Cronジョブを、サーバーがインターフェースから隠す実装の詳細として扱います。それが汎用インターフェースの美しさです。クライアントは、サーバーが舞台裏で何をするかを知る必要はありません。関心があるのは、サービスが要求された状態の変化を理解して応答することだけです。

6
Jordan

ほとんどの人は、この目的のためにPOSTを使用します。 「他のHTTPメソッドが適切でないと思われる場合、安全でない操作または非べき等の操作」を実行するのに適しています。

XMLRPC などのAPIは、POSTを使用して、任意のコードを実行できるアクションをトリガーします。 「アクション」はPOSTデータに含まれています。

POST /RPC2 HTTP/1.0
User-Agent: Frontier/5.1.2 (WinNT)
Host: betty.userland.com
Content-Type: text/xml
Content-length: 181

<?xml version="1.0"?>
<methodCall>
   <methodName>examples.getStateName</methodName>
   <params>
      <param>
         <value><i4>41</i4></value>
         </param>
      </params>
   </methodCall>

RPCは、POSTがサーバー側メソッドのHTTP動詞の従来の選択であることを示すための例です。 POSTでのロイフィールディングの考え方 -彼は、指定されたHTTPメソッドを使用することがRESTfulであると言っています。

RPC自体はリソース指向ではないため、あまりRESTfulではありません。ただし、ステートレス、キャッシュ、またはレイヤー化が必要な場合は、適切な変換を行うのは難しくありません。 http://blog.perfectapi.com/2012/opinionated-rpc-apis-vs-restful-apis/ を参照してください。

3

POST設計されたHTTPメソッド

データのブロックを提供しています...データ処理プロセスに

CRUDにマッピングされていないアクションを処理するサーバー側のメソッドは、RESTで Roy Fieldingが意図した であるため、あなたはそこにいるので、POSTは非べき等性になりました。 POSTは、情報を処理するサーバー側メソッドへのデータのほとんどの投稿を処理します。

つまり、犬のbarえ声のシナリオでは、サーバー側の樹皮を10分ごとに実行したいが、何らかの理由でトリガーがクライアントから発生する必要がある場合、PUTはそのべき等性のために目的を果たします。まあ、厳密にはこのシナリオでは、複数のPOSTリクエストが犬に代わって鳴き声を出すという明らかなリスクはありませんが、とにかく2つの同様の方法の目的です。 同様のSO質問に対する私の答え が役に立つかもしれません。

2
Jacob Stevens

いくつかの回答の以前の改訂では、RPCの使用が提案されました。 RPCに目を向ける必要はありません。REST制約を順守しながら、必要なことを完全に行うことができます。

まず、URLにアクションパラメーターを入れないでください。 URLは、アクションを適用するwhatを定義し、クエリパラメータはURLの一部です。完全にnoun。http://api.animals.com/v1/dogs/1/?action=barkhttp://api.animals.com/v1/dogs/1/とは異なるリソース、つまり異なる名詞です。 [n.b. Askerは?action=bark URIを質問から削除しました。]たとえば、http://api.animals.com/v1/dogs/?id=1http://api.animals.com/v1/dogs/?id=2を比較します。クエリ文字列によってのみ区別されるさまざまなリソース。そのため、リクエストのアクションは、ボディのない既存のメソッドタイプ(TRACE、OPTIONS、HEAD、GET、DELETEなど)に直接対応しない限り、リクエストボディで定義する必要があります。

次に、アクションが「 idempotent 」であるかどうかを判断します。これは、悪影響なしで繰り返すことができることを意味します(詳細については次の段落を参照)。たとえば、クライアントが目的の効果が発生したかどうか不明な場合、値をtrueに設定することを繰り返すことができます。彼らは再びリクエストを送信し、値はtrueのままです。数値に1を追加しても、べき等ではありません。クライアントがAdd1コマンドを送信した場合、それが機能していることを確認してから再度送信した場合、サーバーは1つまたは2つ追加しましたか?それが決まったら、メソッドに対してPUTPOSTのどちらかを選択するのがよいでしょう。

べき等とは、結果を変更せずにリクエストを繰り返すことができることを意味します。これらの影響には、ロギングやその他のサーバー管理アクティビティは含まれません。最初と2番目の例を使用すると、同じ人に2つの電子メールを送信すると、1つの電子メールの送信とは異なる状態になります(受信者の受信ボックスに2つあり、スパムと見なされる可能性があります)ので、間違いなくPOST。例2のbarkCountがAPIのユーザーに表示されることを意図している場合、またはクライアントから見えるものに影響を与える場合、リクエストをide等ではありません。自分だけが表示する場合は、サーバーロギングとしてカウントされ、べき等性を決定する場合は無視する必要があります。

最後に、実行するアクションがすぐに成功するかどうかを判断します。 BarkDogは、すぐに完了するアクションです。 RunMarathonはそうではありません。アクションが遅い場合は、ユーザーがポーリングしてアクションが完了したかどうかを確認するために、応答本文にURLを含む202 Acceptedを返すことを検討してください。または、ユーザーにPOSTを/marathons-in-progress/などのリストURLに移動させ、アクションが完了したら、進行中のID URLから/marathons-complete/ URLにリダイレクトします。
特定のケース#1および#2の場合、サーバーにキューをホストさせ、クライアントはアドレスのバッチをポストします。アクションはSendEmailsではなく、AddToDispatchQueueのようなものになります。サーバーはキューをポーリングして、待機中のメールアドレスがあるかどうかを確認し、見つかった場合はメールを送信します。次に、キューを更新して、保留中のアクションが実行されたことを示します。クライアントにキューの現在の状態を示す別のURIがあります。電子メールの二重送信を回避するために、サーバーは、この電子メールの送信者のログを保持し、各アドレスをチェックして、POST同じリストをキューに2回。

何かのURIを選択するときは、アクションではなく結果として考えてみてください。たとえば、google.com/search?q=dogsは、「dogs」という単語の検索のresultsを示します。必ずしも検索を実行するわけではありません。

リストのケース#3と#4もi等のアクションではありません。提案されているさまざまな効果がAPIの設計に影響する可能性があることを提案します。 4つすべてのケースで、4つすべてが「ワールドステート」を変更するため、同じAPIを使用します。

1
Nicholas Shanks

Barkingが、消費者が行動できる内部/依存/サブリソースであると仮定すると、次のように言えます。

POST http://api.animals.com/v1/dogs/1/bark

犬番号1の樹皮

GET http://api.animals.com/v1/dogs/1/bark

最後の樹皮のタイムスタンプを返します

DELETE http://api.animals.com/v1/dogs/1/bark

適用されません!無視してください。

1
bolbol

私の 新しい回答を参照してください-これはこれと矛盾し、RESTとHTTPをより明確かつ正確に説明します。

ここにrecommendationがあります。これはたまたまRESTfulですが、確かに唯一のオプションではありません。サービスがリクエストを受け取ったときにbarえ始めるには:

POST /v1/dogs/1/bark-schedule HTTP/1.1
...
{"token": 12345, "next": 0, "frequency": 10}

tokenは、このリクエストが何度送信されても​​冗長な鳴き声を防ぐ任意の数字です。

nextは次の樹皮の時間を示します。 0の値は 'ASAP'を意味します。

GET /v1/dogs/1/bark-scheduleを取得するたびに、次のようになります。tは最後の樹皮の時間で、はt + 10分:

{"last": t, "next": u}

同じURLを使用して、犬の現在のほえる状態を調べるために使用する樹皮を要求することを強くお勧めします。 RESTに必須ではありませんが、スケジュールを変更する行為を強調しています。

適切なステータスコードは、おそらく 205 です。現在のスケジュールを見るクライアントが、POSTsを同じURLに変更してそれを変更し、変更されたことを証明するためにスケジュールを再確認するようにサービスによって指示されるクライアントを想像しています。

説明

REST

しばらくHTTPを忘れてください。 resource は入力として時間がかかり、identifiersrepresentationsを含むセットを返す関数であることを理解することが不可欠です。それを単純化してみましょう:リソースは、識別子と表現のセットRです。 Rは変更可能-メンバーは追加、削除、または変更できます。 (識別子を削除または変更するのは悪い、不安定な設計ですが。)Rの要素である識別子はRを識別し、その表現はRの要素はRを表します。

Rは犬だとしましょう。 R/v1/dogs/1として識別します。 (意味/v1/dogs/1Rのメンバーです。)これはRを特定できる多くの方法の1つにすぎません。 R/v1/dogs/1/x-raysおよび/v1/rufusとして識別することもできます。

Rをどのように表現しますか?たぶん写真付き。多分X線のセットで。または、Rが最後にbarえられた日付と時刻を示すこともできます。しかし、これらはすべて同じリソースの表現であることを忘れないでください。 /v1/dogs/1/x-raysは、「いつR最後の樹皮?」という質問に対する答えで表される同じリソースの識別子です。

HTTP

必要なものを参照できない場合、リソースの複数の表現はあまり役に立ちません。これがHTTPが便利な理由です。HTTPを使用すると、 識別子を表現に接続 できます。つまり、サービスがURLを受信し、クライアントに提供する表現を決定する方法です。

少なくとも、それはGETがすることです。 PUTは、基本的にGETの逆です:あなたはPUT表現r将来のそのURLへのGETリクエストを希望する場合、URLでreturn r、JSONからHTMLへの変換などが考えられます。

POSTは、表現を変更するより緩やかな方法です。互いに対応する表示ロジックと修正ロジックがあると考えてください。両方とも同じURLに対応しています。 POSTリクエストは、サービスが適切と判断した場合に、情報を処理し、任意の表現(同じURLによって特定される表現だけでなく)を変更するための変更ロジックの要求です。 9.6 PUT の後の3番目の段落に注意してください。URLの内容を新しいコンテンツに置き換えているわけではありません。 URLにあるものに何らかの情報を処理し、有益な表現の形でインテリジェントに応答するように求めています。

この場合、/v1/dogs/1/bark-schedule(最後にbarえたときと次にandえるときを告げる表示ロジックに対応する)の修正ロジックに情報を処理し、それに応じていくつかの表現を修正します。将来のGETsに応じて、同じURLに対応する表示ロジックは、犬が希望どおりにbarえていることを知らせます。

Cronジョブを実装の詳細と考えてください。 HTTPは、表現の表示と変更を扱います。これ以降、サービスは、犬が最後にbarえたときと次にnextえるときをクライアントに通知します。サービスの観点からすると、これらの時間は過去および計画されたcronジョブに対応するため、正直です。

0
Jordan