私は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コールになる可能性があるオプションをいくつか紹介します。
RESTfulの原則は、Webサイトを簡単にする機能(ランダムな人間のユーザーが"surf"するために)をWebサービスのAPIデザインに持ち込むことで、プログラマにとって使いやすいものにします。RESTはRESTだから良いのではなく、RESTだから良いのです そして、それが良いのは、ほとんどがシンプルだからです。
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つの方法(最も単純な方法と言う人もいます)は、**アクションではなくリソースの観点からアプリケーションを考えることです。
それでは、デザイン案を見てみましょう。
ACTION http://api.animals.com/v1/dogs/1/
まず最初に、新しいHTTP動詞 (ACTION
) を作成することを検討すべきではありません。一般的に言って、これはいくつかの理由から 望ましくありません 。
ACTION
動詞が存在することを知ることができるでしょうか?POST
** を使うことを検討してみましょう (理由は後述します。今は私の言葉を信じてください)。POST /v1/dogs/1/ HTTP/1.1
Host: api.animals.com
{"action":"bark"}
これはOKかもしれませんが、以下の場合のみです。
URI は "dog" リソース (おそらく
id==1の犬) を表し、ドキュメントプロセッサを表すものではありません。 つまり、今わかっているのは、上記のデザインはあまりRESTfulではないということですが、それは具体的にどのようなことなのでしょうか?**基本的に、それは複雑な意味を持つ複雑なURIであるため、悪いことなのです。そこから何かを推論することはできません。プログラマはどうやって犬が
barkというアクションを持っていて、それに
POST` を密かに注入できることを知ることができるでしょうか?そこで、本題に入り、リソースの観点から考えて、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}`の下にあるため)。
さて、あなたのリストのそれぞれのケースは、特定の動作を持っています。dog.email
にメールを送るだけで、何も記録しません。まず、吠える(メールを送る)のは同期タスクなのか非同期タスクなのでしょうか?次に、bark
のリクエストは何かドキュメント(メールかもしれません)を必要としますか?それとも空ですか?
dog.email
にメールを送信し、何も記録しません(同期タスクとして)このケースは単純です。ファクトリーリソース barks
を呼び出すと、すぐに吠え声(メールの送信)が聞こえ、その応答(OKかどうか)がすぐに与えられます。
POST /v1/dogs/1/barks HTTP/1.1
ホスト:api.animals.com
認可を取得します。ベーシック mAUhhuE08u724bh249a2xaP=
(エンティティボディは空です。または、**ドキュメント**が必要な場合は、ここに置いてください)
200 OK
何も記録(変更)しないので、`200 OK`で十分です。これは、すべてが期待通りに行ったことを示します。
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`でキャンセルすることもできるかもしれません。
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` が返される必要があります。
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" することができます。
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`が適用されます。
ACTION http://api.animals.com/v1/dogs/1/?action=bark
以下、その理由を説明します。 クライアントがデータを使ってサーバーにWHAT TO DOを伝える方法が、メソッド情報です。
http://www.google.com/search?q=DOG
を見てみましょう。そこでは、メソッド情報は GET
で、スコープ情報は /search?q=DOG
である。
長い話になります。POST
を使えばいいんですよ。これは問題なく動作しますし、最もシンプルな方法かもしれませんが、 しかしこれはRESTfulではありません。
あなたのサービスを本当にRESTfulに保つためには、一歩下がって、ここで本当にやりたいこと(リソースにどんな影響を与えるか)について考えなければならないかもしれません。
あなたの具体的なビジネスニーズについて話すことはできませんが、一例を挙げましょう。例えば、RESTfulな注文サービスを考えてみましょう。注文は example.com/order/123
のようなURIにあります。
ここで、注文をキャンセルしたいとします。どうすればいいでしょうか?これは "cancellation" "action" であると考え、POST example.com/order/123?do=cancel
として設計したくなるかもしれません。
これは、上で話したように、RESTfulではありません。代わりに、true
に送られたcanceled
要素を持つorder
の新しい表現をPUT
することができます。PUT /order/123 HTTP/1.1
Content-Type: application/xml
<order id="123">
<customer id="89987">...</customer>
<canceled>true</canceled>
...
</order>
そして、それだけです。注文をキャンセルできない場合は、特定のステータスコードを返すことができます。 (単純化するために、POST /order/123/canceled
のようにエンティティボディ true
を持つサブリソース設計も可能でしょう。)
あなたの特定のシナリオでは、似たようなことを試すことができます。そうすれば、例えば犬が吠えている時に /v1/dogs/1/
で GET
すると、その情報を含むことができます (例えば <barking>true</barking>
) .あるいは...それが複雑すぎる場合は、RESTfulな要件を緩和して、POST
にこだわってください。
しかし、アルゴリズム(アクション)をリソースのセットとして公開するコツをつかむのにしばらく時間がかかるのです。アクション("地図上の場所を検索する")で考えるのではなく、アクションの結果(*"検索に一致する地図上の場所のリスト)で考える必要があるのです。
という条件を満たした場合です。)
もしあなたのデザインがHTTPの統一されたインターフェースに合わないことがわかったら、このステップに戻ってくるかもしれません。
クエリ変数は スコープ情報 ですが、新しいリソースを示すものではありません (/post?lang=ja
は明らかに /post?lang=jp
と同じリソースで、表現が異なるだけです)。むしろ、これらは クライアントの状態 (例えば ?page=10
のように、サーバーに状態を保持させないようにします。 ?lang=ja
もこの例です) や アルゴリズムリソース** (/search?q=dogs
, /dogs?code=1
) への入力パラメータとして使用されます。繰り返しますが、明確なリソースではありません。
URIの?action=something
がRESTfulでないことを示すもう一つの明確なポイントは、HTTP動詞の特性です。
と
HEAD` は安全です (そしてべき乗です)。と
DELETE` はべき乗のみです。はどちらとも言えません。 **安全性**:安全性**:
GETや
HEADリクエストはあるデータを ** 読む** ためのリクエストであり、サーバーの状態を変更するためのリクエストではありません。クライアントは
GETや
HEADのリクエストを10回行っても、1回行ったのと同じであり、また全く行わないのと同じです。 **Idempotence**:冪等性**: 1度だけ適用しても、2度以上適用しても同じ効果を持つ操作のことです(数学ではゼロの掛け算は冪等性です)。例えば、あるリソースを一度
DELETEしたら、もう一度削除しても同じ効果があります(そのリソースは既に
GONEになっています)。 <sup><sub>
POSTは安全でもべき等でもありません。同一の
POSTリクエストを 'factory' リソースに 2 回行うと、おそらく同じものを含む 2 つの下位リソースが作成されます。 情報を提供します。オーバーロードされた(URIやentity-bodyのメソッド)
POSTでは、すべての賭けが外れます。 これらの特性は両方とも、HTTPプロトコルの成功にとって重要でした(信頼性の低いネットワーク上で!):ページが完全に読み込まれるまで待たずに、何回ページを更新(
GET`)しましたか?
アクション*を作成し、それをURLに配置することは、明らかにHTTPメソッドを壊します'契約。もう一度言いますが、技術的には可能ですが、それはRESTfulなデザインではありません。ほとんどの人は、この目的のために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"?>
<methodCall>
<methodName>examples.getStateName</methodName>
<params>
<param>
<value><i4>41</i4></value>
</param>
</params>
</methodCall>
RPCは、POSTがサーバーサイドのメソッドのためのHTTP動詞の従来の選択であることを示すために例証されています。 ここで、Roy Fielding thoughts on POST -- 彼は、HTTPメソッドを指定通りに使用することがRESTfulであると、かなり言っています。
なお、RPC自体はリソース指向ではないので、あまりRESTfulではありません。 しかし、ステートレス、キャッシュ、レイヤリングが必要な場合、適切な変換を行うことは難しくありません。 例として、http://blog.perfectapi.com/2012/opinionated-rpc-apis-vs-restful-apis/ を参照してください。