1. ホーム
  2. rest

[解決済み] RESTfulな方法でリソースのサーバーサイドメソッドを呼び出す

2022-04-26 13:23:06

質問

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

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

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

URLをリクエストします。

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

リクエストボディに

{"action":"bark"}

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

edit for clarification

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

  1. barkはdog.emailにメールを送信するだけで、何も記録しません。
  2. barkはdog.emailにメールを送り、dog.barkCountを1だけ増加させます。
  3. barkは、吠えが発生した日時を記録したbark.timestampを使用して、新しい"bark"レコードを作成します。また、dog.barkCountを1だけ増加させます。
  4. bark はシステムコマンドを実行し、Github から最新バージョンの dog コードを取得します。そして dog.owner にテキストメッセージを送り、新しい dog コードが実稼働していることを伝えます。

解決するには?

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

RESTfulの原則 Webサイトを簡単にする機能をもたらす ランダムな人間のユーザー を使用する) ウェブサービスAPIの設計に そのため、プログラマーにとって使いやすいものとなっています。 RESTはRESTだから良いのではなく、良いから良いのです。 そして、それが良いのは、ほとんどが シンプル .

プレーンなHTTPのシンプルさ(SOAPエンベロープやシングルURIのオーバーロードがない)。 POST サービス)、何 と呼ぶ人もいます。 機能不足。 は、実際には <強い 最大の強み . HTTPでは、まず最初に アドレス指定可能 ステートレス この2つの基本的な設計上の決定により、今日のメガサイト(およびメガサービス)に対応するHTTPのスケーラビリティが維持されています。

しかし、RESTは銀の雄たけびを上げるものではありません。 時にはRPCスタイルの ("Remote Procedure Call" - SOAPなど)。 適切な場合があります。 そして、時にはWebの良さよりも他のニーズが優先されることもあります。これはいいことです。私たちがあまり好きではないのは は、不必要な複雑さ . プログラマや企業が、古いHTTPで十分に処理できる仕事にRPCスタイルのサービスを持ち込むことがあまりに多いのです。その結果、HTTPは、何が起こっているのかを説明する巨大なXMLペイロードのためのトランスポートプロトコルに成り下がってしまいます(URIやHTTPメソッドは、それについての手がかりを与えてくれません)。その結果、サービスはあまりに複雑で、デバッグが不可能になり、クライアントが 正確な設定 開発者が意図したとおりに

同じようにJava/C#のコードも ない HTTPを使うだけでは、RESTfulなデザインとは言えません。という焦りにとらわれるかもしれません。 考える サービスについて アクションとリモートメソッドの観点から を呼び出す必要があります。これでは、ほとんどがRPC-Styleのサービス(またはREST-RPC-hybrid)になってしまうのも無理はありません。最初のステップは、違う考え方をすることです。RESTfulな設計は多くの方法で実現できます。 アプリケーションをアクションではなく、リソースの観点から考える。

<ブロッククオート

???? 地図上の場所を検索する」というようなアクションを考えるのではなく

...の観点から考えるようにします。 結果 これらのアクション("検索条件に一致する地図上の場所のリスト")について。

以下、例を挙げて行きます。 (RESTの他の重要な側面は、HATEOASの使用です。 別の記事で .)



第一設計の課題

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

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

これは 可能性 はいいのですが 場合のみ :

  • {"action":"bark"} は文書であった。
  • /v1/dogs/1/ は、quot;document processor"(工場的)なURIでした。 <サブ document processor"とは、ただ物を投げて("srowing")そのことを忘れてしまうようなURIのことです。例えば、メッセージブローカーサービスにメッセージを投稿するためのURIで、投稿後にメッセージの処理状況を示すURIにリダイレクトされるようなものです。

あなたのシステムについて詳しくは知らないが、両方とも真実でないことは間違いないだろう。

  • {"action":"bark"} はドキュメントではありません というのは、実際には は、メソッド にしようとしている。 ニンジャスニーク サービスへ参入する。
  • その /v1/dogs/1/ URIは、"dog"リソースを表します(おそらく、犬は id==1 ) であり、ドキュメントプロセッサではありません。

さて、今わかっていることは、上のデザインはあまりRESTfulではないということですが、具体的にはどうなのでしょうか? 何がそんなに悪いのか? 基本的には、複雑な意味を持つ複雑なURIであることが悪いのです。そこから何かを推論することはできません。プログラマはどうやって犬が bark というアクションを密かに注入することができます。 POST を入れるのですか?



質問のAPIコールを設計する

では、本題に入り、これらのバークをREST的にデザインしてみることにしよう。 リソースの観点から . を引用させてください。 レストフルウェブサービス の本に書かれています。

A POST リクエストは、既存のリソースから新しいリソースを作成する試みです。 があります。既存のリソースは、新しいリソースの親になる可能性があります。 データ構造の意味で、ツリーのルートがすべての そのリーフノードの あるいは、既存のリソースが特別な "ファクトリー"。 リソースは、他のリソースを生成することだけが目的である。そのため と共に送信される表現 POST リクエストは初期状態を記述します。 の状態を表示します。PUTと同様に POST リクエストは必要ありません。 は表現を全く含まない。

上記の記述に従うと、次のようになります。 bark は次のようにモデル化することができます。 のサブリソースです。 dog (ある bark は犬の中に含まれる、つまり、吠えるは "barked"。 によって a dog)である。

その推論から、私たちはすでに

  • このメソッドは POST
  • リソースは /barks dog のサブリソースです。 /v1/dogs/1/barks を表します。 bark ファクトリー"です。このURIはそれぞれの犬に対して一意であり、(それは /v1/dogs/{id} ).

これで、リストの各ケースが特定の動作をするようになりました。

##1. バークはメールを送信するだけです。 dog.email で、何も記録しない。

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



1.1 バークがメールを送る先 dog.email を実行し、何も記録しない(同期タスクとして)

このケースは単純です。への呼び出しは barks ファクトリーリソースはすぐに吠え声 (メールの送信) を出し、レスポンス (OK かどうか) もすぐに与えられます。

POST /v1/dogs/1/barks HTTP/1.1
Host: api.animals.com
Authorization: Basic mAUhhuE08u724bh249a2xaP=

(entity-body is empty - or, if you require a **document**, place it here)

200 OK

何も記録(変更)しないため。 200 OK で十分です。すべてが期待通りに進んだことを示すのです。



1.2 barkがメールを送る先 dog.email を実行し、何も記録しない(非同期タスクとして)

この場合、クライアント側で bark タスクになります。その bark タスクは、それ自身のURIを持つリソースである必要があります。

POST /v1/dogs/1/barks HTTP/1.1
Host: api.animals.com
Authorization: Basic mAUhhuE08u724bh249a2xaP=

{document body, if needed;
NOTE: when possible, the response SHOULD contain a short hypertext note with a hyperlink
to the newly created resource (bark) URI, the same returned in the Location header
(also notice that, for the 202 status code, the Location header meaning is not
standardized, thus the importance of a hipertext/hyperlink response)}

202 Accepted
Location: http://api.animals.com/v1/dogs/1/barks/a65h44

こうすることで、各 bark は追跡可能である。そして、クライアントは GET から bark URIを使用して、現在の状態を知ることができます。もしかしたら DELETE をクリックするとキャンセルされます。



2.バークは、次のようなメールを送信します。 dog.email をインクリメントし、さらに dog.barkCount 1ずつ

これはもっと厄介なことで、もしクライアントが dog リソースが変更されます。

POST /v1/dogs/1/barks HTTP/1.1
Host: api.animals.com
Authorization: Basic mAUhhuE08u724bh249a2xaP=

{document body, if needed; when possible, containing a hipertext/hyperlink with the address
in the Location header -- says the standard}

303 See Other
Location: http://api.animals.com/v1/dogs/1

この場合 location ヘッダーの意図は、クライアントに dog . からは に関するHTTP RFC。 303 :

このメソッドが存在するのは、主に POST -起動スクリプト を使用して、ユーザーエージェントを選択したリソースにリダイレクトします。

タスクが非同期の場合は bark と同じようにサブリソースが必要です。 1.2 の状況と 303 で返す必要があります。 GET .../barks/Y タスクが完了したとき



3.バークは、新しい"を作成します。 bark というレコードを作成します。 bark.timestamp は、吠えが発生した時刻を記録する。また dog.barkCount を1ずつ加算していきます。

POST /v1/dogs/1/barks HTTP/1.1
Host: api.animals.com
Authorization: Basic mAUhhuE08u724bh249a2xaP=

(document body, if needed)

201 Created
Location: http://api.animals.com/v1/dogs/1/barks/a65h44

この中で bark はリクエストによって作成されたものなので、ステータス 201 Created が適用されます。

作成が非同期の場合は 202 Accepted が必要です ( HTTP RFCにあるように )の代わりに

保存されたタイムスタンプは bark リソースで取得することができます。 GET を追加しました。更新された犬は、その中に "documented"することができます。 GET dogs/X/barks/Y と同じです。



4. バークはシステムコマンドを実行し、Githubから最新版のdogコードを引き降ろす。その後、テキストメッセージを dog.owner 新しいdogのコードが本番になったことを伝える。

この文言は複雑ですが、かなり単純な非同期タスクです。

POST /v1/dogs/1/barks HTTP/1.1
Host: api.animals.com
Authorization: Basic mAUhhuE08u724bh249a2xaP=

(document body, if needed)

202 Accepted
Location: http://api.animals.com/v1/dogs/1/barks/a65h44

このときクライアントは GET/v1/dogs/1/barks/a65h44 で現在の状態(コードが抜かれたのか、飼い主にメールが送られたのか、など)を知ることができます。犬が変わるたびに 303 が適用可能です。



まとめ

引用元 ロイ・フィールディング :

RESTがメソッドに要求する唯一のことは、メソッドが統一されていることです。 すべてのリソースに対して定義されている(つまり、仲介者がそのリソースを使用する必要がない)。 の意味を理解するために、リソースの種類を知ることができます。 のリクエストに対応します)。

上記の例では POST は統一されたデザインです。これで、犬の" bark "です。これは安全ではありませんし(吠えることがリソースに影響を与えるという意味)、べき等でもありません(各リクエストは新しい bark ) に適合する。 POST の動詞がよく出てきます。

プログラマーなら知っているはず:a POST から barksbark . レスポンスステータスコード(必要に応じてエンティティボディとヘッダも)は、何が変更され、クライアントがどのように処理できるのか、また処理すべきなのかを説明する役割を果たします。

<サブ 注:使用した主なソースは以下の通り: " レストフルウェブサービス という本があります。 HTTP RFC ロイ・フィールディングのブログ .






編集する

質問とその回答は、最初に作成されたときとはかなり変わっています。その 元の質問 のようなURIの設計について質問しています。

ACTION http://api.animals.com/v1/dogs/1/?action=bark

以下、その理由を説明します。

クライアントがサーバーに伝える方法 何をするか は、データに対して メソッド情報 .

  • RESTfulなウェブサービスは、HTTPメソッドでメソッド情報を伝える。
  • 一般的なRPC-StyleやSOAPサービスでは、エンティティボディとHTTPヘッダにその内容を保持します。

どの部分 クライアントがサーバーに操作させたい]データの スコープ情報 .

  • RESTfulなサービスではURIを使用します。SOAP/RPC-Style サービスでは、再び entity-body と HTTP ヘッダーを使用します。

例として、GoogleのURIを挙げてみよう。 http://www.google.com/search?q=DOG . そこでは、メソッド情報は GET で、スコープ情報は /search?q=DOG .

長い話ですが

  • RESTfulアーキテクチャ の場合、メソッド情報はHTTPメソッドに入ります。
  • リソース指向アーキテクチャー で、スコープ情報はURIに入る。

そして経験則。

HTTPメソッドとメソッド情報が一致しない場合、そのサービスはRESTfulではありません。スコープ情報がURIにない場合、そのサービスはリソース指向ではありません。

をつけることができます。 "bark"。 "アクション" をURL内(またはエンティティボディ内)に記述し POST . これは問題なく動作しますし、最もシンプルな方法かもしれません。 しかし、これはRESTfulではありません .

あなたのサービスを本当にRESTfulに保つためには、一歩下がって、ここで本当にやりたいこと(リソースにどんな影響を与えるか)を考える必要があるかもしれません。

あなたの具体的なビジネスニーズについて話すことはできませんが、例を挙げましょう。RESTful な注文サービスでは、注文は次のような URI で行われます。 example.com/order/123 .

さて、注文をキャンセルしたい場合、どのようにすればよいのでしょうか?と考えたくなるかもしれませんが、それは キャンセル "アクション" というようにデザインします。 POST example.com/order/123?do=cancel .

これでは、上で話したようにRESTfulとは言えません。代わりに、私たちは PUT の新しい表現である order を持つ canceled 要素に送られます。 true :

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 は、簡略化のため、利用可能であってもよい)。

あなたの具体的なシナリオでは、似たようなことを試してみるとよいでしょう。そうすれば、例えば犬が吠えている間に GET/v1/dogs/1/ は、その情報を含めることができます (例) <barking>true</barking> ) . あるいは......それがあまりに複雑な場合は、RESTfulな要件を緩めて POST .

アップデートする

あまり答えを大きくしたくないのですが、アルゴリズムを公開するコツをつかむのに時間がかかるので、(1)(2)(3)(4)(5)(6)のようにしました。 アクション をリソースのセットとして使用します。アクションの観点で考えるのではなく、( 地図上の場所を検索してください。 )、その行動の結果という観点で考える必要がある( 検索条件に一致する地図上の場所のリスト。 を選択します。 ).

もし、あなたのデザインがHTTPの統一されたインターフェイスに合わないことがわかったら、このステップに戻ってくることになるかもしれません。

クエリ変数 スコープ情報 を使用しますが ではなく 新しいリソースを表す ( /post?lang=en は明らかに 同じ リソースとして /post?lang=jp 表現が違うだけです)。むしろ、これらは クライアントの状態 (例えば ?page=10 というように、その状態はサーバーに保持されない。 ?lang=en もその一例です)または 入力パラメータ から アルゴリズミック・リソース ( /search?q=dogs , /dogs?code=1 ). ここでも、個別のリソースではありません。

HTTP動詞の(メソッドの)プロパティ。

を示すもう一つの明確なポイント ?action=something がRESTfulでないのは、HTTP動詞の特性によるものです。

  • GETHEAD は安全(かつべき乗)である。
  • PUTDELETE はべき乗のみである。
  • POST はどちらでもない。

安全性 : A GET または HEAD のリクエストは 読む あるデータ、サーバーの状態を変更するリクエストではありません。クライアントは GET または HEAD を10回リクエストすれば、1回作るのと同じになる、あるいは 全く作らない .

べき乗 : べき乗の演算とは、一度だけ適用しても二度以上適用しても同じ効果を持つ演算のことです(数学ではゼロの掛け算がべき乗になります)。もし、あなたが DELETE を一度削除すれば、再度削除しても同じ効果が得られます(リソースは GONE を使用します)。

<サブ POST は安全でもなければべき乗でもない。つの同じ POST ファクトリー'リソースへのリクエストは、おそらく、同じ 情報を提供します。オーバーロードされた(URIまたはentity-bodyのメソッド)場合 POST は、すべての賭けに出る。

この2つの特性は、HTTPプロトコルの成功にとって(信頼性の低いネットワーク上で!)重要なものでした:あなたは何回( GET )が完全に読み込まれるまで待たずに、ページを表示することができますか?

を作成する アクション をURL内に配置することは、明らかにHTTPメソッドの契約を破ることになります。もう一度言いますが、技術的には可能ですが、それはRESTfulな設計ではありません。