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

私はRESTについて初歩的な理解を持っていることに留意してください。例えば、以下のようなURLがあるとします。

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

そして今、私はサーバーに犬を吠えさせたいのです。この方法はサーバーだけが知っています。例えば、10分ごとに犬を吠えさせるCRONジョブを永遠に実行させたいとします。その呼び出しはどのようなものでしょうか?私はこれをやってみたいんです。

URLを要求します。

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

リクエストボディに

{"action":"bark"}

自分でHTTPメソッドを作ったことを怒られる前に、RESTfulな方法でサーバーサイドのメソッドを呼び出す方法について、もっと良いアイデアを教えてください:)

明確化のための編集

bark"メソッドが何をするのかについて、もう少し明確にしました。異なる構造のAPIコールになる可能性があるオプションをいくつか紹介します。

  1. bark は dog.email にメールを送信するだけで、何も記録しません。
  2. dog.email にメールを送信し、dog.barkCount を 1 だけ増加させる。
  3. barkは新しい"bark"レコードを作成し、bark.timestampに吠えた時の記録を残します。また、dog.barkCountを1つ増やします。
  4. Githubから最新バージョンのdogのコードを取得するためにシステムコマンドを実行します。そして dog.owner にテキストメッセージを送信し、新しい dog コードが本番環境にあることを伝えます。
ソリューション

なぜRESTfulな設計を目指すのか?

RESTfulの原則は、Webサイトを簡単にする機能ランダムな人間のユーザーが"surf"するために)をWebサービスのAPIデザインに持ち込むことで、プログラマにとって使いやすいものにします。[RESTはRESTだから良いのではなく、RESTだから良いのです][1] そして、それが良いのは、ほとんどがシンプルだからです。 SOAPエンベロープや単一URIのオーバーロードされた POST サービスがない)プレーンなHTTPのシンプルさは、人によっては "lack of features" と呼ぶかもしれませんが、実は その最大の強み なのです。この2つの基本的な設計上の決定が、今日のメガサイト(とメガサービス)にまでHTTPのスケーラビリティを維持しているのです。 しかし、RESTは銀の雄たけびを上げるものではありません。時にはRPCスタイル(SOAPなどのRemote Procedure Call")が適切な場合もあり、他のニーズがWebの良さよりも優先される場合もあります。これはいいことです。私たちが本当に嫌なのは、不必要な複雑さです。プログラマーや企業が、古いHTTPで十分に処理できる仕事を、RPCスタイルのサービスで行うことがあまりに多いのです。その結果、HTTPは、何が起こっているのかを説明する巨大なXMLペイロードのためのトランスポートプロトコルに成り下がってしまいます(URIやHTTPメソッドは、それについての手がかりを与えません)。その結果、サービスはあまりにも複雑で、デバッグが不可能になり、クライアントが開発者の意図通りの 正確な設定 をしていない限り、動作しません。 Java/C#のコードがオブジェクト指向でないのと同じように、HTTPを使用するだけではRESTfulなデザインにはなりません。アクションとリモートメソッドという観点から、自分のサービスを考えることに夢中になるかもしれません。これでは、RPCスタイルのサービス(またはREST-RPC-ハイブリッド)になってしまうのも無理はない。最初のステップは、異なる考え方をすることです。RESTfulな設計は多くの方法で達成することができます。1つの方法(最も単純な方法と言う人もいます)は、**アクションではなくリソースの観点からアプリケーションを考えることです。

  • アクションの観点から考えるのではなく、("地図上の場所を検索する")。
  • アクションの結果("検索条件に一致する地図上の場所のリスト")という観点で考えてみてください。 以下はその例です。 (RESTの他の重要な側面は、HATEOASの使用です。ここでは書きませんが、別の投稿で手短に話しています[2])。

    最初のデザインについて

    それでは、デザイン案を見てみましょう。

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

まず最初に、新しいHTTP動詞 (ACTION) を作成することを検討すべきではありません。一般的に言って、これはいくつかの理由から 望ましくありません

  • (1)** サービスURIだけが与えられたとき、プログラマはどうやって ACTION 動詞が存在することを知ることができるでしょうか?
  • (2)** もしプログラマーがその存在を知っていたとして、どうやってそのセマンティクスを知ることができるでしょうか?その動詞は何を意味するのでしょうか?
  • (3)** その動詞が持つべき性質(安全性、べき等)は何か?
  • (4)** プログラマが標準的なHTTP動詞しか扱わない、非常にシンプルなクライアントを持っていたらどうでしょう?
  • (5) ... では、POST** を使うことを検討してみましょう (理由は後述します。今は私の言葉を信じてください)。
POST /v1/dogs/1/ HTTP/1.1
Host: api.animals.com

{"action":"bark"}

これはOKかもしれませんが、以下の場合のみです。

  • action":"bark"}` がドキュメントであり、かつ
  • v1/dogs/1/` は、quot;ドキュメントプロセッサ(工場)のようなURIです。 A document processor" は、ただ物を投げてquot;忘れるようなURIです。例えば、メッセージブローカーサービスにメッセージを投稿するためのURIで、投稿後にメッセージの処理状況を示すURIにリダイレクトされるようなものです。 あなたのシステムについて詳しくは知らないのですが、両方が真実でないことは間違いないでしょう。
  • はドキュメントではなく、あなたがサービスに忍び込もうとしているメソッドです。
  • v1/dogs/1/URI は "dog" リソース (おそらくid==1の犬) を表し、ドキュメントプロセッサを表すものではありません。 つまり、今わかっているのは、上記のデザインはあまりRESTfulではないということですが、それは具体的にどのようなことなのでしょうか?**基本的に、それは複雑な意味を持つ複雑なURIであるため、悪いことなのです。そこから何かを推論することはできません。プログラマはどうやって犬がbarkというアクションを持っていて、それにPOST` を密かに注入できることを知ることができるでしょうか?

    質問のデザイン'APIコール

    そこで、本題に入り、リソースの観点から考えて、RESTfulに吠え声をデザインしてみましょう。Restful Web Services][3]の本を引用させてください。 POSTリクエストは、既存のリソースから新しいリソースを作成する試みである。 gt;一つです。既存のリソースは、新しいリソースの親になる可能性があります。 データ構造的な意味で、ツリーのルートがすべての その葉ノード。あるいは、既存のリソースは、特別な *"factory"* である可能性があります。 他の資源を生み出すことだけを目的とした資源。このリソースは POST リクエストと一緒に送信される表現には、初期の 新しいリソースの状態です。PUT と同様に、 POST リクエストは、以下のことを行う必要はありません。 表現が含まれます。 上記の説明から、bark`dog のサブリソースとしてモデル化できることがわかります(bark は dog の中に含まれているので、つまり、bark は "barked" by** a dog であるため)。 この推論から、我々はすでに

  • メソッドは POST です。
  • リソースは /barks で、dog のサブリソースである。v1/dogs/1/barksで、これはbarkの "factory" を表します。このURIはそれぞれの犬に対して一意です(/v1/dogs/{id}`の下にあるため)。 さて、あなたのリストのそれぞれのケースは、特定の動作を持っています。

    1. bark は dog.email にメールを送るだけで、何も記録しません。

    まず、吠える(メールを送る)のは同期タスクなのか非同期タスクなのでしょうか?次に、barkのリクエストは何かドキュメント(メールかもしれません)を必要としますか?それとも空ですか?

    1.1 barkはdog.emailにメールを送信し、何も記録しません(同期タスクとして)

    このケースは単純です。ファクトリーリソース barks を呼び出すと、すぐに吠え声(メールの送信)が聞こえ、その応答(OKかどうか)がすぐに与えられます。

    POST /v1/dogs/1/barks HTTP/1.1
    ホスト:api.animals.com
    認可を取得します。ベーシック mAUhhuE08u724bh249a2xaP=
    (エンティティボディは空です。または、**ドキュメント**が必要な場合は、ここに置いてください)
    200 OK
    
    何も記録(変更)しないので、`200 OK`で十分です。これは、すべてが期待通りに行ったことを示します。
    ### 1.2 樹皮が `dog.email` にメールを送り、何も記録しない (非同期タスクとして) この場合、クライアントは `bark` タスクを追跡する方法を持たなければなりません。その場合、`bark`タスクはそれ自身のURIを持つリソースである必要があります。
    POST /v1/dogs/1/barks HTTP/1.1
    ホスト:api.animals.com
    認可を取得します。ベーシック mAUhhuE08u724bh249a2xaP=
    {ドキュメント本体、必要な場合。
    注:可能な場合、レスポンスにはハイパーリンクを含む短いハイパーテキストメモを含めるべきである(SHOULD)。
    新しく作成されたリソース(樹皮)のURI(Locationヘッダで返されるものと同じ)へ
    (ステータスコード202の場合、Locationヘッダーの意味が異なることに注意)
    標準化されているため、ハイパーテキスト/ハイパーリンクの応答が重要である)}。
    202 受け入れました。
    場所:http://api.animals.com/v1/dogs/1/barks/a65h44
    
    この方法では、それぞれの`bark`を追跡することができます。クライアントは `bark` の URI に対して `GET` を発行して、現在の状態を知ることができます。もしかしたら、`DELETE`でキャンセルすることもできるかもしれません。
    ## 2. バークは `dog.email` にメールを送り、`dog.barkCount` を 1 だけ増やします。 もし、`dog` リソースが変更されたことをクライアントに知らせたい場合、これはより厄介なことになります。
    POST /v1/dogs/1/barks HTTP/1.1
    ホスト:api.animals.com
    認可を取得します。ベーシック mAUhhuE08u724bh249a2xaP=
    {必要な場合はドキュメント本体、可能な場合はハイパーテキスト/ハイパーリンクを含む、アドレス
    は、Locationヘッダに含まれています。}
    303 その他参照
    場所:http://api.animals.com/v1/dogs/1
    
    この場合、`location`ヘッダ'の意図は、クライアントに`dog`を見るべきだということを知らせることです。HTTP RFC about `303`][4] からです。 > このメソッドは主に POST`で起動されたスクリプト**は、ユーザーエージェントを選択したリソースにリダイレクトします。 タスクが非同期の場合、 `1.2` の場合と同様に `bark` サブリソースが必要で、タスクが完了したら `GET .../barks/Y` で `303` が返される必要があります。
    ## 3. bark は新しい "`bark`" レコードを作成し、`bark.timestamp` が吠えた時の記録を作成します。また、`dog.barkCount`を1つ増加させます。
    POST /v1/dogs/1/barks HTTP/1.1
    ホスト:api.animals.com
    認可を取得します。ベーシック mAUhhuE08u724bh249a2xaP=
    (必要であれば、ドキュメント本体)
    201 作成されました。
    場所:http://api.animals.com/v1/dogs/1/barks/a65h44
    
    ここでは、リクエストによって `bark` が作成されたので、ステータス `201 Created` が適用されています。 非同期で作成された場合は、代わりに `202 Accepted` が必要です([HTTP RFC に記載されています][5])。 保存されたタイムスタンプは `bark` リソースの一部であり、 `GET` で取得することができます。更新された犬は `GET dogs/X/barks/Y` の中で "documented" することができます。
    ## 4. bark は Github から最新版の dog のコードを取得するためにシステムコマンドを実行します。そして `dog.owner` に新しい dog のコードが本番環境にあることを伝えるテキストメッセージを送ります。 この文言は複雑ですが、ほとんど単純な非同期タスクです。
    POST /v1/dogs/1/barks HTTP/1.1
    ホスト:api.animals.com
    認可を取得します。ベーシック mAUhhuE08u724bh249a2xaP=
    (必要であればドキュメント本体)
    202 受け入れました。
    場所:http://api.animals.com/v1/dogs/1/barks/a65h44
    
    クライアントは `/v1/dogs/1/barks/a65h44` に `GET`s を発行して、現在の状態(コードが引き出された場合、オーナーにメールが送られた場合など)を知ることができるようになります。犬が変わるたびに、`303`が適用されます。
    ## ラッピング 引用元: [ロイ・フィールディング][6]。 RESTがメソッドに要求する唯一のことは、メソッドが統一されていることです。 すべてのリソースに対して定義されている(つまり、仲介者が の意味を理解するために、リソースの種類を知る必要があります。 リクエスト)。 上記の例では、 `POST` は一律に設計されています。これは、犬を "`bark`" させます。これは安全ではありませんし(吠えることはリソースに影響を与えるという意味)、べき等でもありません(それぞれのリクエストが新しい `bark` を生成します)。これは `POST` という動詞によく当てはまります。 プログラマーなら、 `barks` への `POST` は `bark` を生成する、ということが分かるでしょう。レスポンスのステータスコード(必要であればエンティティボディやヘッダも)は、何が変わったのか、クライアントはどうすればいいのかを説明する役割を担っています。 Note: 使用した主なソースは、"[Restful Web Services][3]" book, [HTTP RFC][7] and [Roy Fielding's blog][8].
解説 (20)

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

XMLRPC][1] のような API は、任意のコードを実行できるアクションを起動するために POST を使用します。 POSTデータにはquot;action"が含まれます。


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"?>

   examples.getStateName
   <params>
      <param>
         41
解説 (6)

RESTはリソース指向の規格であり、RPCのようなアクションドリブンではない。

もし、サーバーにbarkさせたいなら、JSON-RPCのような別のアイデアや、websocket通信に目を向けるべきでしょう。

私の意見では、RESTful に保とうとすると失敗します。action パラメータで POST を発行すれば、新しいリソースを作成するわけではありませんが、副作用があるかもしれないので、より安全だと思います。

解説 (2)