REST APIの概念


10

REST APIの設計について3つの質問があります。誰かに光を当ててもらいたいと思っています。私は何時間も執拗に検索しましたが、私の質問に対する答えがどこにも見つかりませんでした(たぶん、何を検索すればいいのか分かりませんか?)。

質問1

私の最初の質問は、アクション/ RPCに関するものです。しばらくREST APIを開発していて、コレクションやリソースの観点から物事を考えることに慣れています。しかし、私はパラダイムが適用されないように見えるいくつかのケースに遭遇し、これをRESTパラダイムと調整する方法があるかどうか疑問に思っています。

具体的には、リソースを変更するとメールが生成される場合があります。ただし、後で、ユーザーは以前に送信された電子メールを再送信することを具体的に示すことができます。電子メールを再送信しても、リソースは変更されません。状態は変更されません。発生する必要があるのは単にアクションです。アクションは特定のリソースタイプに関連付けられています。

ある種のアクション呼び出しをリソースURI(例:)と混合することは適切/collection/123?action=resendEmailですか?アクションを指定してそれにリソースIDを渡す方がよいでしょうか(例:)/collection/resendEmail?id=123?これはそれについて取り組むのに間違った方法ですか?従来(少なくともHTTPでは)実行されるアクションはリクエストメソッド(GET、POST、PUT、DELETE)ですが、これらは実際にはリソースを使用したカスタムアクションを許可していません。

質問2

URLのクエリ文字列部分を使用して、コレクションをクエリするときに返されるリソースのセットをフィルター処理します(例:)/collection?someField=someval。次に、APIコントローラー内で、そのフィールドと値とどのような比較を行うかを決定します。これは実際には機能しないことがわかりました。APIユーザーが実行する比較のタイプを指定できる方法が必要です。

私がこれまでに作ってみた最高のアイデアは、APIのユーザーはフィールド名(例えばへの付属物として、それを指定することができるようにすることです/collection?someField:gte=somevalどこでリソースを返す必要があることを示すために- someField何でも(> =)以上に等しいsomevalです。これは良いアイデアですか?悪いアイデアですか?そうであれば、なぜですか?指定されたフィールドと値で実行する比較のタイプをユーザーが指定できるようにするより良い方法はありますか?

質問3

s /person/123/dogsを取得するようなURIがよく見られます。最終的に私はそのようなURIを作成することで、実際には特定のIDでフィルターされたコレクションにアクセスしているだけだと考えているので、私は一般にそのようなものを避けました。これはと同等です。REST URIが2つ以上のレベル()になる正当な理由はありますか?persondogsdogsperson/dogs?person=123/collection/resource_id


10
3つの質問があります。それらを別々に投稿してみませんか?
anaximander 2013

3
これを3つの個別の質問に分けた方がよいでしょう。視聴者は、1つではなくすべての質問に優れた答えを出すことができる場合があります。

2
それらはすべて関連していると思います。タイトルは少しハイレベルですが、この質問は多くの人に役立ち、SE検索中に簡単に見つかります。十分な票と実質が追加されたら、この質問はコミュニティWikiになるはずです。このものを研究するのに数週間かかった。
Andrew T Finnell 2013

1
IDKさん、別々に投稿した方がいいかもしれません。ただし、@ AndrewFinnellが述べたように、REST関連の質問はこれまでで最も難しいため、他の人が答えを見つけられるようになっているので、質問をまとめておくことをお勧めします一緒。
Justin Warkentin 2013

回答:


11

ある種のアクション呼び出しをリソースURI(例:)と混合することは適切/collection/123?action=resendEmailですか?アクションを指定してそれにリソースIDを渡す方がよいでしょうか(例:)/collection/resendEmail?id=123?これはそれについて取り組むのに間違った方法ですか?従来(少なくともHTTPでは)実行されるアクションはリクエストメソッド(GET、POST、PUT、DELETE)ですが、これらは実際にはリソースを使用したカスタムアクションを許可していません。

送信するメールを表すリソースのコレクションを使用して、別の方法でモデル化したいと思います。送信はやがてサービスの内部で処理され、その時点で対応するリソースが削除されます。(または、ユーザーがリソースを早期に削除して、送信を行う要求をキャンセルする可能性があります。)

リソース名に動詞を含めないでください。それが名詞です(クエリの部分は形容詞のセットです)。名詞動詞はRESTを奇妙にします!

URLのクエリ文字列部分を使用して、コレクションをクエリするときに返されるリソースのセットをフィルター処理します(例:)/collection?someField=someval。次に、APIコントローラー内で、そのフィールドと値とどのような比較を行うかを決定します。これは実際には機能しないことがわかりました。APIユーザーが実行する比較のタイプを指定できる方法が必要です。

これまでに思いついた最良のアイデアは、APIユーザーがフィールド名の付属物としてそれを指定できるようにすることです(たとえば/collection?someField:gte=someval-someFieldが(>=)に等しいかそれ以上のリソースを返す必要があることを示すため)someval。これは良い考えですか?悪い考えですか?もしそうなら、なぜですか?与えられたフィールドと値で実行する比較のタイプをユーザーが指定できるようにするより良い方法はありますか?

一般的なフィルター句を指定し、コレクションのコンテンツを取得するリクエストのオプションのクエリパラメーターとして指定します。その後、クライアントは、返されるセットを制限する方法を、希望する方法で正確に指定できます。また、フィルター/クエリ言語の発見可能性についても少し心配します。金持ちが多ければ多いほど、任意のクライアントが発見することが難しくなります。少なくとも理論的にはその発見可能性の問題に対処する別のアプローチは、コレクションの制限サブリソースを作成できるようにすることです。クライアントは、コレクションリソースへの制限を説明するドキュメントをPOSTすることによって取得します。それはまだ少し虐待ですが、少なくともあなたが明らかに発見可能にすることができるものです!

この種の発見可能性は、RESTで私が最も得意としないものの1つです。

私はよく/person/123/dogs人の犬を取得するようなものに見えるURIを見つけます。最終的に私はそのようなURIを作成することで、実際には特定の個人IDでフィルターされた犬のコレクションにアクセスしているだけだと考えているので、私はそのようなものを避けました。これはと同等/dogs?person=123です。REST URIが2つ以上のレベル(/collection/resource_id)になる正当な理由はありますか?

ネストされたコレクションが実際に外部コレクションのメンバーエンティティのサブ機能である場合、それらをサブリソースとして構造化するのが妥当です。「サブ機能」とは、UMLの構成関係のようなものを意味します。外部リソースを破棄すると、当然、内部コレクションも破棄されます。

他のタイプのコレクションは、HTTPリダイレクトとしてモデル化できます。したがって/person/123/dogs、リダイレクトする307を実行することで、実際に応答でき/dogs?person=123ます。この場合、コレクションは実際にはUML構成ではなく、UML集約です。違いは重要です。それは重要です!


2
全体的にしっかりしたポイントがあります。ただし、resendEmailコレクションを作成してそれにPOSTすることでアクションを処理することはできますが、それはあまり自然ではないようです。実際、私はメールが再送信されたときにデータベースに何も保存しません(必要ありません)。リソースは変更されないため、単に成功または失敗するアクションです。呼び出しの寿命を超えて存在するリソースIDを返すことができなかったため、そのような実装はRESTfulではなくハックになりました。これは単にCRUD操作ではありません。
Justin Warkentin 2013

3

大企業がREST APIを設計する方法をすべて見てきたので、RESTを適切に使用する方法について少し混乱しているのは理解できます。

RESTはリソースコレクションシステムです。これは、Representational State Transferの略です。あなたが私に尋ねるなら、素晴らしい定義ではありません。しかし、主な概念は4つのHTTP VERBであり、ステートレスであることです。

注意すべき重要な部分は、RESTで使用できるVERBSは4つだけであることです。これらは、GET、POST、PUT、DELETEです。あなたのresend例は、RESTに新しい動詞を追加することです。これは危険信号です。

質問1

REST APIの呼び出し元はPUT、コレクションに対してを実行すると電子メールが生成されることを知る必要がないことを理解することが重要です。それは私に漏れのにおいがします。彼らが知ることができるのは、を実行すると、PUT後でクエリできる余分なタスクが発生する可能性があるということです。彼らはGET、最近作成されたリソースでを実行することでこれを知っています。これGETにより、Taskリソースとそれに関連付けられているすべてのリソースID が返されます。その後、それらのタスクをクエリしてステータスを確認し、新しいを送信することもできTaskます。

いくつかのオプションがあります。

REST-タスクリソースベースのアプローチ

tasks特定のタスクをシステムに送信してアクションを実行できるリソースを作成します。次にGETID返されたタスクに基づいてタスクを実行し、ステータスを判断できます。

またはSOAP over HTTP、アーキテクチャにRPCを追加するためにWebサービスを混在させることができます。

特定のリソースのすべてのタスクを照会する

GET http://server/api/myCollection/123/tasks

{ "tasks" :
    [ { "22333" : "http://server/api/tasks/223333" } ] 
}

タスクリソースの例

PUT http://server/api/tasks

{ 
    "type" : "send-email" , 
    "parameters" : 
    { 
         "collection-type" : "foo" , 
         "collection-id" : "123" 
    } 
}

==>タスクのIDを返します

223334

GET http://server/api/tasks/223334

{ 
    "status" : "complete" , 
    "date" : "whenever" 
}

REST-POSTを使用してアクションをトリガーする

POSTリソースにはいつでもデータを追加できます。私の意見では、これはRESTの精神に違反しますが、それでも準拠します。

次のようなPOSTを実行できます。

POST http://server/api/collection/123

{ "action" : "send-email" }

追加のデータでコレクションからリソース123を更新します。その追加データは基本的に、そのリソースの電子メールを送信するようにバックエンドに指示するアクションです。

これに関して私が抱えている問題はGET、リソースのがこの更新されたデータを返すことです。ただし、これは要件を解決し、RESTfulです。

SOAP-RESTから取得したリソースを受け入れるWebサービス

REST APIからの以前のリソースIDに基づいて電子メールを送信できる新しいWebServiceを作成します。元の質問はRESTに関するものなので、ここではSOAPについて詳しく説明しません。これらの2つの概念/技術は、AppleとOrangesであるため、比較しないでください。

質問2

ここにはいくつかのオプションもあります。

REST APIを公開している多くの大企業がsearchコレクションを公開しているようですが、コレクションは、クエリパラメーターを渡してリソースを返す方法にすぎません。

GET http://server/api/search?q="type = myCollection & someField >= someval"

次のような完全修飾RESTリソースのコレクションを返します。

{
    "results" : 
       { [ 
             "location" : "http://server/api/myCollection/1",
             "location" : "http://server/api/myCollection/9",
             "location" : "http://server/api/myCollection/56"
         ]
       }
}

または、MVELなどをクエリパラメータとして許可することもできます。

質問3

前に戻ってクエリパラメータを使用して他のリソースにクエリを実行する必要があるよりも、サブレベルを好みます。なんらかのルールがあるとは思いません。両方の方法を実装して、発信者が最初にシステムに入力した方法に基づいて、どちらがより適切であるかを決定できるようにすることができます。

ノート

他の人からの読みやすさのコメントに同意しません。RESTはまだ人間の消費用ではないと考える人もいるでしょう。機械消費用です。ツイートを見たい場合は、Twitterの通常のWebサイトを使用します。APIでREST GETを実行しません。プログラムでツイートを処理したい場合は、REST APIを使用します。はい、APIは理解できる必要がありますgteが、それほど悪くはありません。直感的ではありません。

RESTのもう1つの重要な点は、事前に他のリソースの正確なURLを知らなくても、APIの任意の時点から開始して、他のすべての関連リソースに移動できることです。GETREST のVERB の結果は、それが参照するリソースの完全なREST URLを返す必要があります。したがって、PersonオブジェクトのIDを返すクエリではなく、などの完全修飾URLを返しますhttp://server/api/people/13。その後、URLが変更された場合でも、プログラムで常に結果をナビゲートできます。

コメントへの応答

実際には、リソースの作成、読み取り、更新、または削除(CRUD)を行わない必要があることが実際にあります。

リソースに対して追加のアクションを実行できます。一般的なリレーショナルデータベースは、ストアドプロシージャの概念をサポートしています。これらは、一連のデータに対して実行できる追加のコマンドです。RESTには本質的にその概念はありません。そして、そうすべき理由はありません。これらのタイプのアクションは、RPCまたはSOAP Webサービスに最適です。

これは、REST APIで見られる一般的な問題です。開発者は、RESTを取り巻く概念上の制限を気に入らないため、RESTを適応させて、RESTを好きなように実行できます。ただし、RESTfulサービスになることはできません。基本的に、これらのURL GETは疑似RESTのようなサーブレットの呼び出しになります。

いくつかのオプションがあります:

  • タスクリソースを作成する
  • POSTアクションを実行するためのリソースへの追加データのサポート。
  • SOAP Webサービスを介して追加のコマンドを追加します。

クエリパラメータを使用した場合、どのHTTP VERBを使用してメールを再送信しますか?

  • GET-これはメールを再送信し、リソースのデータを返しますか?システムがそのURLをキャッシュし、そのリソースの一意のURLのように処理した場合はどうなるでしょうか。URLにアクセスするたびに、メールが再送信されます。
  • POST -実際には新しいデータをリソースに送信せず、追加のクエリパラメータを送信しました。

与えられたすべての要件に基づいPOSTて、action fieldas POSTデータでリソースに対してを実行すると、問題が解決します。


3
HTTPを介して実装されたRESTはこれらの4つの動詞を提供しますが、これらの動詞がそれの終わりであるとは確信していません。実際には、リソースの作成、読み取り、更新、または削除(CRUD)を行わない必要があることが実際にあります。メールの再送信もその1つです。データベースに何かを保存したり変更したりする必要はありません。それは単に成功または失敗するアクションです。
Justin Warkentin 2013

@JustinWarkentin私はあなたのニーズが何であるかを理解しています。しかし、それでもRESTはそうではありません。URLに新しい動詞を追加することは、RESTアーキテクチャに反します。RESTfulになる別の代替案を提供するために、回答を更新します。
Andrew T Finnell 2013

@JustinWarkentin私の答えの「REST-POSTを使用してアクションをトリガーする」を確認してください。
Andrew T Finnell 2013

0

質問1:ある種のアクション呼び出しをリソースURIと混合することは適切ですか?または、アクションを指定してリソースIDをそれに渡す方が良いでしょうか?

良い質問。この場合、後者の方法、つまりアクションを指定してリソースIDを渡すことをお勧めします。このようにして、リソースが最初に変更されると、リソースは/sendEmailアクション(別注:「再送信」を呼び出す必要はありません)を個別のRESTfulリクエストとして呼び出します(変更されるリソースとは関係なく、後で何度でも呼び出すことができます) )。

質問2:次のような比較演算子の使用について:/collection?someField:gte=someval

これは技術的には問題ありませんが、おそらく悪い考えです。RESTの主要な原則の1つは読みやすさです。たとえば、比較演算子を別のパラメーターとして単に渡すことをお勧めします。/collection?someField=someval&operator=gteもちろん、デフォルトのケースに対応できるようにAPIを設計します(operatorパラメーターがURIから除外されている場合)。

質問3: REST URIが2レベル以上の深さである正当な理由はありますか?

うん; 抽象化のため。私は、例えば、複数のURIのレベルを介して抽象化層を利用カップルのREST APIを見てきました:/vehicles/cars/123または/vehicles/bikes/123今度はあなたが両方に関する有用な情報で作業することができます/vehiclesし、/vehicles/bikesコレクション。そうは言っても、私はこのアプローチの大ファンではありません。実際にこれを行う必要はほとんどありません。たった2つのレベルを使用するようにAPIを再設計できる可能性があります。

そして、はい、上記のコメントが示唆するように、将来的には質問を別々の投稿に分割するのが最善です;)


私の質問2の例は単純すぎました。1つだけでなく、コレクションのフィルター処理に使用される各フィールドの比較演算子を指定する必要があるため、例ではのようにする必要があります/collection?field1=someval&field1Operator=gte&field2=someval&field2Operator=eq
Justin Warkentin 2013

0

質問2の場合、別の代替案の方がより柔軟な場合があります。各検索を、ユーザーが使用する前に作成したリソースと見なします。

あなたが「検索」コンテナを持っているとしましょう、そこであなたはPOST /api/searches/コンテンツのクエリ仕様で行います。それは、JSON、XML、またはSQLドキュメントでさえあり得ます。クエリが正しく解析されると、独自のURIを持つ新しいリソースとして新しい検索が作成されます。/api/searches/q123/

次に、クライアントGET /api/searches/q123/はクエリ結果を取得するだけです。

最後に、クライアントにクエリを削除するように要求するか、セッションを閉じた後にそれを削除することができます。


0

なんらかのアクション呼び出しをリソースURIと混在させることは適切ですか(例:/ collection / 123?action = resendEmail)?アクションを指定してリソースIDを渡す方が良いでしょうか(例:/ collection / resendEmail?id = 123)?これはそれについて取り組むのに間違った方法ですか?従来(少なくともHTTPでは)実行されるアクションは要求メソッド(GET、POST、PUT、DELETE)ですが、これらは実際にはリソースを使用したカスタムアクションを許可していません。

IRIはリソースではなく操作を識別するためのものであるため、適切ではありません(ただし、PPLがこのメソッドオーバーライドアプローチをしばらく使用している場合(非POSTおよびGETメソッドの使用がサポートされていない場合)。できることは、適切なHTTPメソッドを探すか、新しいメソッドを作成することです。これらの場合、POSTはあなたの友達になることができます(適切なメソッドが見つからず、要求が取得されない場合は、pplを使用します)。メール送信からリソースを作成するためのもう1つの方法はPOST /emails、実際のリソースを作成せずにメールを送信できるようにすることです。ところで URI構造はセマンティクスを持たないため、RESTの観点からは、使用するURIの種類は実際には問題になりません。重要なのは、クライアントに送信したリンクに割り当てられたメタデータ(リンク関係など)です。

これまでに思いついた最良のアイデアは、APIユーザーがフィールド名の付加としてそれを指定できるようにすることです(たとえば/ collection?someField:gte = someval-someFieldがより大きいリソースを返す必要があることを示すため)これは良いアイデアですか?悪いアイデアですか?そうであれば、なぜですか?指定されたフィールドと値で実行する比較のタイプをユーザーが指定できるようにするためのより良い方法はありますか?

独自のクエリ言語を作成する必要はありません。私はむしろ既存のものを使用して、リンクのメタデータにクエリの説明を追加します。そのためには、おそらくRDFメディアタイプ(JSON-LDなど)を使用するか、カスタムMIMEタイプを使用する必要があります(これをサポートする非RDF形式はありません)。既存の標準を使用すると、クライアントをサーバーから切り離すことができます。これが、統一されたインターフェース制約に関するものです。

/ dogs?person = 123と同等です。REST URIが2レベル以上(/ collection / resource_id)になる正当な理由はありますか?

前述したように、URI構造はRESTの観点からは重要ではありません。/x71fd823df2たとえば使用できます。クライアントはURI構造ではなくリンクに割り当てられたメタデータをチェックするため、それでもクライアントには意味があります。URIの主な目的は、リソースを識別することです。URI標準では、パスには階層データが含まれ、クエリには非階層データが含まれると記載されています。しかし、それは階層的であるものは非常に主観的であることができます。そのため、複数のレベルの深いURIや長いクエリを使用したURIに対応します。

私は何時間も執拗に検索しましたが、私の質問に対する答えがどこにも見つかりませんでした(たぶん、何を検索すればいいのか分かりませんか?)。

少なくともFieldingの論文の​​REST制約HTTP標準、そしておそらくMarkusの第3世代のWeb APIを読む必要があります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.