REST APIのベストプラクティス:パラメーター値のリストを入力として受け入れる方法[終了]


409

私たちは新しいREST APIをローンチしており、入力パラメーターをどのようにフォーマットするかについてのベストプラクティスに関するコミュニティの意見を求めていました。

現在、APIはJSON中心です(JSONのみを返します)。XMLを返す必要があるかどうかの議論は別の問題です。

私たちのAPI出力はJSON中心であるため、私たちは入力が少しJSON中心であるという道をたどっています。

たとえば、現在複数の製品を同時にプルできるいくつかの製品の詳細を取得するには、次のようにします。

http://our.api.com/Product?id=["101404","7267261"]

これを次のように簡略化する必要があります。

http://our.api.com/Product?id=101404,7267261

それともJSON入力が便利ですか?もっと痛い?

両方のスタイルを受け入れたいと思うかもしれませんが、その柔軟性は実際により多くの混乱と頭痛(メンテナンス性、ドキュメントなど)を引き起こしますか?

より複雑なケースは、より複雑な入力を提供したい場合です。たとえば、検索で複数のフィルターを許可する場合:

http://our.api.com/Search?term=pumas&filters={"productType":["Clothing","Bags"],"color":["Black","Red"]}

フィルタータイプ(productTypeやcolorなど)をリクエスト名として次のように指定する必要はありません。

http://our.api.com/Search?term=pumas&productType=["Clothing","Bags"]&color=["Black","Red"]

すべてのフィルター入力をグループ化したかったからです。

結局、これは本当に重要ですか?JSONのユーティリティが非常に多く存在するため、入力タイプはそれほど重要ではない可能性があります。

APIへのAJAX呼び出しを行うJavaScriptクライアントは、JSON入力を理解して、より簡単に生活できるようになることを知っています。

回答:


341

一歩戻る

まず第一に、RESTはURIを普遍的に一意のIDとして記述します。非常に多くの人々がURIの構造に追いついて、どのURIが他のURIよりも「安らかな」ものになっています。 この議論は、誰かに「ボブ」という名前を付けることは、「ジョー」という名前よりも優れていると言うのと同じくらい馬鹿げています。 URIは、普遍的に一意の名前にすぎません。

したがって、RESTの目に?id=["101404","7267261"]は、がより安らかである?id=101404,7267261か、それとも\Product\101404,7267261いくらか無駄であるかどうかについての議論があります。

とは言っても、URIがどのように構築されるかは、通常、RESTfulサービスの他の問題の優れた指標として役立ちます。一般的に、URIと質問にはいくつかの問題があります。

提案

  1. 同じリソースの複数のURIおよび Content-Location

    両方のスタイルを受け入れたいと思うかもしれませんが、その柔軟性は実際により多くの混乱と頭痛(メンテナンス性、ドキュメントなど)を引き起こしますか?

    URIはリソースを識別します。各リソースには1つの正規URI が必要です。これは、2つのURIが同じリソースを指すことができないという意味ではありませんが、それを行うための明確に定義された方法あります。JSONとリストベースの形式(またはその他の形式)の両方を使用する場合は、これらの形式のどれが主要な正規 URIであるかを決定する必要があります。同じ「リソース」を指す他のURIへのすべての応答には、Content-Locationヘッダーを含める必要があります。

    名前の類似性にこだわり、複数のURIを持つことは、人々のニックネームを持つようなものです。それは完全に受け入れ可能で、多くの場合非常に便利ですが、ニックネームを使用している場合でも、おそらく彼らのフルネーム、つまりその人を参照する「公式」の方法を知りたいと思っています。このように、誰かが「Nichloas Telsa」というフルネームで誰かに言及するとき、私は「Nick」と私が呼ぶのと同じ人物について話していることを知っています。

  2. URIでの「検索」

    より複雑なケースは、より複雑な入力を提供したい場合です。たとえば、検索で複数のフィルターを許可したい場合...

    私の一般的な経験則は、URIに動詞が含まれている場合、何かが無効であることを示している可能性があります。URIはリソースを識別しますが、そのリソースに対して実行していることを示すべきではありません。それがHTTPの仕事、または落ち着いた言葉で言えば、私たちの「統一されたインターフェース」です。

    名前の類推を打ち負かすために、URIで動詞を使用することは、対話したいときに誰かの名前を変更するようなものです。ボブとやり取りしていると、こんにちはと言いたいときにボブの名前が「BobHi」にならない。同様に、製品を「検索」する場合、URI構造を「/ Product / ...」から「/ Search / ...」に変更しないでください。

最初の質問に答える

  1. ["101404","7267261"]vs について101404,7267261:ここでの提案は、簡単にするためにJSON構文を回避することです(つまり、本当に必要のないときにユーザーがURLエンコードを行う必要はありません)。それはあなたのAPIを少し使いやすくします。さらに良いのは、他の人が推奨しているように、application/x-www-form-urlencodedおそらくエンドユーザー(たとえば?id[]=101404&id[]=7267261)に最も慣れている標準形式を使用することです。「きれい」ではないかもしれませんが、プリティURIは必ずしも使用可能なURIを意味するわけではありません。ただし、私の最初のポイントを繰り返しますが、最終的にRESTについて話すとき、それは問題ではありません。あまり深く考えすぎないでください。

  2. 複雑な検索URIの例は、製品の例とほとんど同じ方法で解決できます。このapplication/x-www-form-urlencodedフォーマットはすでに多くの人がよく知っている標準なので、もう一度やり直すことをお勧めします。また、2つをマージすることをお勧めします。

あなたのURI ...

/Search?term=pumas&filters={"productType":["Clothing","Bags"],"color":["Black","Red"]}    

URIエンコードされた後のURI ...

/Search?term=pumas&filters=%7B%22productType%22%3A%5B%22Clothing%22%2C%22Bags%22%5D%2C%22color%22%3A%5B%22Black%22%2C%22Red%22%5D%7D

に変換することができます...

/Product?term=pumas&productType[]=Clothing&productType[]=Bags&color[]=Black&color[]=Red

URLエンコードの要件を回避し、物事をもう少し標準的に見せることを除いて、APIを少し均質化します。ユーザーは、製品または製品のリスト(両方ともRESTful用語では単一の「リソース」と見なされます)を取得したい場合、/Product/...URIに関心があることを知っています。


67
フォローアップして、[]構文が常にサポートされているわけではないことに注意してください(一般的であるにもかかわらず、URI仕様に違反している場合もあります)。一部のHTTPサーバーとプログラミング言語は、名前を繰り返すだけを好むでしょう(例:)productType=value1&productType=value2
nategood 2013

1
このクエリの最初の質問.. "/ Search?term = pumas&filters = {" productType ":[" Clothing "、" Bags "]、" color ":[" Black "、" Red "]}"は.. 。(productType == clothing || productType == bags)&&(color == black || color == red)しかし、あなたのソリューション:/ Product?term = pumas&productType [] = Clothing&productType [] = Bags&color [] = Black&color [] =赤は次のように変換されるようです...(productType == clothing || productType == bags || color == black || color == red)または(productType == clothing && productType == bags && color ==黒&& color == red)これは私には少し違うようです。それとも私は誤解しましたか?
Thomas Cheng、

2
投稿リクエストの入力はどうですか?リソースを更新するかどうかを知りたいのですが、クエリ/フィルターと本文のデータを標準形式で送信することは悪い習慣です。たとえば。API /user/を使用して本文に含まれるユーザーに関連するデータを変更する場合は、ユーザーと一緒にクエリを送信{ q:{}, d: {} }qます。ユーザーは、DBでd変更されたデータとしてクエリされます。
分子

1
リストが非常に大きい場合はどうしますか?ブラウザによっては、URIの長さが制限されています。私は通常、投稿リクエストに切り替えて、リストを本文で送信しました。何か提案はありますか?
Troy Cosentino

4
非常に大きくする必要がありますが(stackoverflow.com/questions/417142/…を参照)、はい、最も極端な場合は、リクエストの本文を使用する必要があります。データ取得のためのクエリのPOSTは、RESTafarianが議論することを好むものの1つです。
nategood 2017

233

値のリストをURLパラメータとして渡す標準的な方法は、それらを繰り返すことです。

http://our.api.com/Product?id=101404&id=7267261

ほとんどのサーバーコードはこれを値のリストとして解釈しますが、多くの場合、単一値の簡略化があるため、調べに行く必要があるかもしれません。

区切り値も問題ありません。

JSONをサーバーに送信する必要がある場合、URLに表示するのは好きではありません(これは形式が異なります)。特に、URLにはサイズ制限があります(理論上ではないとしても、実際には)。

一部の人がRESTfulに複雑なクエリを実行するのを見た方法は、2つのステップです。

  1. POST クエリ要件、IDを受け取る(基本的には検索条件リソースを作成する)
  2. GET 上記のIDを参照する検索
  3. 必要に応じてオプションでクエリ要件を削除しますが、それらの要件は再利用できます。

8
キャシーありがとう。私はあなたと一緒にいると思いますが、URLにJSONを表示することもあまり好きではありません。ただし、私は本来のGET操作である検索の投稿を行うのが好きではありません。この例を指摘できますか?
whatupwilly 2010

1
クエリが単純なパラメータとして機能する場合は、それを実行します。出典は、rest-discussメーリングリストからのものです。tech.groups.yahoo.com
Kathy Van Stone

2
あなただけの2つのリソースを表示したい場合は、ジェームズ・ウエストゲートの答えは、より一般的です
キャシー・ヴァン・ストーン

これが正解です。近い将来、ODataまたはそのようなものによってサポートされるfilter = id in(a、b、c、...)が表示されると確信しています。
Bart Calixto 2014年

これがAkka HTTPの動作方法です
Joan

20

最初:

2つの方法でできると思います

http://our.api.com/Product/<id> :1つのレコードだけが必要な場合

http://our.api.com/Product :すべてのレコードが必要な場合

http://our.api.com/Product/<id1>,<id2> :Jamesが提案したように、製品タグの後に続くものがパラメータであるため、オプションにすることができます

または私が最も好きなものは:

ハイパーメディアを RestFul WS のアプリケーション状態のエンジン(HATEOAS)プロパティとして使用し、http://our.api.com/Product同等のURLを返すhttp://our.api.com/Product/<id>呼び出しを実行し、この後にそれらを呼び出すことができます。

二番目

URL呼び出しに対してクエリを実行する必要がある場合。HATEOASをもう一度使用することをお勧めします。

1)get呼び出しを行う http://our.api.com/term/pumas/productType/clothing/color/black

2)get呼び出しを行う http://our.api.com/term/pumas/productType/clothing,bags/color/black,red

3)(HATEOASを使用) ` http://our.api.com/term/pumas/productType/へのget呼び出しを実行します->すべての服の可能なURLを受け取ります->必要なもの(服とバッグ)を呼び出します- >可能な色のURLを受け取る->必要な色のURLを呼び出す


1
私は数日前に同様の状況に置かれました。オブジェクトのフィルタリングされた(大きな)リストを取得するために(HATEOAS)REST APIを調整する必要があり、私は2番目のソリューションを選択しました。それぞれについてAPIを何度も何度も思い起こしていませんか?
サムソン

それは本当にあなたのシステムに依存します...それが少数の「オプション」を持つ単純なものであるなら、それはおそらくやり過ぎでしょう。ただし、非常に大きなリストがある場合、1回の大きな呼び出しですべてを実行するのは非常に面倒になる可能性があります。また、APIがパブリックの場合は、ユーザーにとって複雑になる可能性があります(プライベートの場合は簡単です...知っているユーザーに教えるだけです)。別の方法として、HATEOASと上級ユーザー向けの「ノンレストフルアレイ」コールの両方のスタイルを実装することもできます
Diego Dias

RailsでRESTful API Webサービスを構築しています。上記と同じURL構造(our.api.com/term/pumas/productType/clothing/color/black)に従う必要があります。しかし、それに応じてルートを構成する方法がわかりません。
ルビスト14

用語、製品タイプ、およびコントローラーの色は何ですか?もしそうなら、あなたはただ行う必要があります:resources:term do resources:productType do resources:color end end
Diego Dias

productTypeとcolorはパラメーターです。したがって、productTypeの
パラメーター

12

RFC 6570を調べてみてください。このURIテンプレート仕様は、URLにパラメーターを含める方法の多くの例を示しています。


1
セクション3.2.8が該当するようです。ただし、これは提案された規格であり、その点を超えていないようです。
マイクポスト

3
@MikePost IETFが「標準化」文書の2段階の成熟プロセスに移行したので、「インターネット標準」に移行する前に、6570がさらに数年間このままでいることを期待しています。tools.ietf.org/html/rfc6410 仕様は非常に安定しており、多くの実装があり、広く使用されています。
Darrel Miller

ああ、その変化に気づかなかった。(または、TIL IETFがより妥当になりました。)ありがとうございます!
Mike Post

8

最初のケース:

通常の製品検索は次のようになります

http://our.api.com/product/1

だから私はベストプラクティスはあなたがこれをすることだと思っています

http://our.api.com/Product/101404,7267261

2番目のケース

クエリ文字列パラメータで検索-このように細かい。を使用する代わりに、ANDやORで用語を組み合わせたくなります[]

PSこれは主観的なものになる可能性があるため、快適に感じることを行ってください。

URLにデータを配置する理由は、リンクをサイトに貼り付けたり、ユーザー間で共有したりできるようにするためです。これが問題ではない場合は、代わりにJSON / POSTを使用してください。

編集:リフレクションについて、このアプローチは複合キーを持つエンティティに適していると思いますが、複数のエンティティのクエリには適していません。


3
もちろん、どちらの例でも/、URIはコレクションではなくリソースをアドレス指定しているため、末尾がそこにあってはなりません。
Lawrence Dol、2015年

2
私は常にHTTP動詞を使用していますが、RESTの使用法では特定のアクションを実行することでした。これがガイダンスラインでした。GET:オブジェクトの取得/読み取り、POSTオブジェクトの作成、PUT既存オブジェクトの更新、およびDELETEオブジェクトの削除です。したがって、POSTを使用して何かを取得することはありません。特にオブジェクト(フィルター)のリストが必要な場合は、URLパラメーターのリストを使用してGETを実行します(コンマで区切るとよいようです)
Alex

1

それが完全であり、あなたのニーズを喜ばせているように見えたので、私はnategoodの答えを無視します。ただし、その方法で複数(1つ以上)のリソースを識別することについてコメントを追加したいと思います。

http://our.api.com/Product/101404,7267261

そうすることで、あなたは:

クライアントに あなたの応答を配列として解釈させることでクライアントを複雑化します。これは、次の要求を行うと直観に反します。http://our.api.com/Product/101404

すべての製品を取得するための1つのAPIと、1つ以上を取得するための上記のAPIを使用して、冗長API作成します。UXのためにユーザーに1ページを超える詳細情報を表示しないでください。1つを超えるIDは役に立たず、製品のフィルタリングにのみ使用されると思います。

それはそれほど問題ではないかもしれませんが、単一のエンティティを返すことによってサーバー側でこれを自分で処理する(応答に1つ以上が含まれるかどうかを確認することによって)か、クライアントにそれを管理させる必要があります。

Amazingに本を注文したい。私はそれがどの本であるかを正確に知っており、ホラー本をナビゲートするとリストに表示されます。

  1. 10 000の驚くべきライン、0のすばらしいテスト
  2. 驚くべきモンスターの復活
  3. 素晴らしいコードを複製しましょう
  4. 終わりの驚くべき始まり

2番目の本を選択すると、リストの本の部分を詳しく説明するページにリダイレクトされます。

--------------------------------------------
Book #1
--------------------------------------------
    Title: The return of the amazing monster
    Summary:
    Pages:
    Publisher:
--------------------------------------------

または、その本の完全な詳細のみを表示するページにありますか?

---------------------------------
The return of the amazing monster
---------------------------------
Summary:
Pages:
Publisher:
---------------------------------

私の意見

このリソースの詳細を取得するときに一意性が保証されている場合は、パス変数でIDを使用することをお勧めします。たとえば、以下のAPIは、特定のリソースの詳細を取得する複数の方法を提案しています(製品に一意のIDがあり、その製品の仕様に一意の名前があり、上から下にナビゲートできると仮定します)。

/products/{id}
/products/{id}/specs/{name}

複数のリソースが必要になったときは、より大きなコレクションからフィルタリングすることをお勧めします。同じ例の場合:

/products?ids=

もちろん、これは課されていないので、これは私の意見です。

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