web-dev-qa-db-ja.com

RESTful検索/フィルタリングを設計する方法

私は現在、PHPでRESTful APIを設計および実装しています。しかし、私は自分の初期設計の実装に失敗しました。

GET /users # list of users
GET /user/1 # get user with id 1
POST /user # create new user
PUT /user/1 # modify user with id 1
DELETE /user/1 # delete user with id 1

今のところかなり標準的ですね。

私の問題は最初のものGET /usersです。リストをフィルタリングするためにリクエストボディのパラメータを送信することを検討していました。これは、私が次のように超長いURLを取得せずに複雑なフィルタを指定できるようにしたいからです。

GET /users?parameter1=value1&parameter2=value2&parameter3=value3&parameter4=value4

代わりに、私は以下のようなものが欲しかったです。

GET /users
# Request body:
{
    "parameter1": "value1",
    "parameter2": "value2",
    "parameter3": "value3",
    "parameter4": "value4"
}

これははるかに読みやすく、複雑なフィルタを設定する大きな可能性を与えてくれます。

とにかく、file_get_contents('php://input')GETリクエストのリクエストボディを返しませんでした。私はhttp_get_request_body()も試しましたが、私が使っている共有ホスティングにはpecl_httpがありません。それがとにかく役立ったかどうかわからない。

この質問 そしてGETはおそらくリクエストボディを持つことになっていないことに気づきました。それは少し決定的ではありませんでした、しかし、彼らはそれに対して忠告しました。

だから今何をすべきかわからない。どのようにしてRESTful検索/フィルター関数を設計しますか?

私はPOSTを使用できると思いますが、それはあまりRESTfulではないようです。

383
Erik B

RESTful検索を実装する最良の方法は、検索自体をリソースと見なすことです。検索を作成しているので、その後、POST動詞を使用できます。 POSTを使用するために文字通りデータベースに何かを作成する必要はありません。

例えば:

Accept: application/json
Content-Type: application/json
POST http://example.com/people/searches
{
  "terms": {
    "ssn": "123456789"
  },
  "order": { ... },
  ...
}

ユーザーの観点から検索を作成しています。この実装の詳細は関係ありません。いくつかのRESTful APIは永続性さえ必要としないかもしれません。それが実装の詳細です。

372
Jason Harrelson

GETリクエストでリクエストボディを使用すると、キャッシュシステムがURLのみを使用するため、GETリクエストをキャッシュできないため、RESTの原則に違反します。

さらに悪いことに、あなたのURLはブックマークできません。URLにはユーザーをこのページにリダイレクトするのに必要なすべての情報が含まれていないからです。

リクエストボディパラメータの代わりにURLまたはクエリパラメータを使用してください。

例えば。:

/myapp?var1=xxxx&var2=xxxx
/myapp;var1=xxxx/resource;var2=xxxx 

実際、HTTP RFC 7231には次のように記載されています。

GETリクエストメッセージ内のペイロードには定義済みのセマンティクスがありません。 GETリクエストでペイロードボディを送信すると、一部の既存の実装でリクエストが拒否される可能性があります。

詳細については こちら をご覧ください。

117
jfcorugedo

リソースのフィルタリング/検索はRESTfulな方法で実装できるようです。アイデアは、/filters/または/api/filters/と呼ばれる新しいエンドポイントを導入することです。

このエンドポイントを使用する filter はリソースと見なすことができ、したがってPOSTメソッドで作成できます。このように - もちろん - bodyを使ってすべてのパラメータを運ぶことができ、複雑な検索/フィルタ構造を作成することもできます。

このようなフィルタを作成した後、検索/フィルタ結果を取得するには2つの方法があります。

  1. 一意のIDを持つ新しいリソースが201 Createdステータスコードと共に返されます。それから、このIDを使ってGETリクエストを/api/users/に対して行うことができます。

    GET /api/users/?filterId=1234-abcd
    
  2. POSTを介して新しいフィルタが作成された後は、201 Createdで応答しませんが、303 SeeOtherを指すLocationヘッダと同時に/api/users/?filterId=1234-abcdで応答します。このリダイレクトは、基盤となるライブラリを介して自動的に処理されます。

どちらのシナリオでも、フィルタリングされた結果を取得するために2つの要求を行う必要があります。これは、特にモバイルアプリケーションの場合、欠点と見なされる可能性があります。モバイルアプリケーションのために私は/api/users/filter/への単一のPOST呼び出しを使用するでしょう。

作成したフィルタを保持する方法

それらはDBに格納して後で使用することができます。それらはまた、いくつかの一時的な記憶装置に格納することもできる。再表示していくつかのTTLを指定すると、期限切れになり削除されます。

このアイデアの利点は何ですか?

フィルタ、フィルタ処理された結果はキャッシュ可能で、ブックマークさえもできます。

52
Opal

あなたがやりたいことを達成するための適切なHTTPヘッダがない限り、あなたはリクエストパラメータを使うべきだと思います。 HTTP仕様 は、GETがボディを持つことができないことを明示的には言っていません。しかし この論文 はこう述べている:

慣例により、GETメソッドを使用すると、リソースの識別に必要なすべての情報がURIにエンコードされます。 HTTP / 1.1には、クライアントがURIのクエリ部分ではなくHTTPエンティティボディでサーバにデータを供給する安全な対話(例えば検索)のための規約はない。これは安全な操作のためにはURIが長いかもしれないことを意味します。

13
Daff

初期のAPIが完全にRESTfulであってもそうでなくても(特にアルファ段階にあるときは)、あまり気にしないでください。バックエンドの配管を最初に動作させる。広範囲のテストに対して十分安定したもの( "beta")が得られるまで反復的に洗練しながら、状況を特定するために何らかのURL変換/書き換えをいつでも行うことができます。

あなたは、パラメータがURI自身の位置と規約によってエンコードされているURIを定義することができます。私はPHPを知りませんが、そのような機能が存在すると思います(Webフレームワークを持つ他の言語にも存在します)。

。ストア#1でi = 1..4に対してparam [i] = value [i]で「ユーザー」タイプの検索を行います(value1、value2、value3、...をURI照会パラメーターの省略形として使用)。

1) GET /store1/search/user/value1,value2,value3,value4

または

2) GET /store1/search/user,value1,value2,value3,value4

または次のように(私はそれをお勧めしませんが、後でもっと詳しく)

3) GET /search/store1,user,value1,value2,value3,value4

オプション1では、/store1/search/userという接頭辞が付いたすべてのURIを検索ハンドラ(またはPHP指定のどちらか)にマップします。デフォルトでは、store1の下のリソースを検索します(/search?location=store1&type=userと同じ)。

APIによって文書化され、実施されている規約により、パラメーター値1から4はコンマで区切られ、その順序で表示されます。

オプション2は、位置パラメータ#1として検索タイプ(この場合はuser)を追加します。どちらの選択肢も単なる化粧品の選択です。

オプション3も可能ですが、私はそれが欲しいとは思わない。特定のリソース内での検索の機能は、検索自体に先行するURI自体で提示されるべきであると思います(あたかも検索がリソース内で固有のものであることをURIで明確に示すように)。

URIにパラメータを渡すことに対するこの利点は、検索がURIの一部であることです(したがって、検索はリソースとして処理され、そのコンテンツは時間の経過とともに変化する可能性があります。)パラメータの順序は必須です。 。

このようなことをした後は、GETを使用できます。これは読み取り専用のリソースになります(POSTやPUTはできません。GETされると更新されます)。それはまたそれが呼び出されたときにのみ存在するようになる資源であろう。

また、結果を一定期間キャッシュしたり、DELETEを使用してキャッシュを削除したりすることで、セマンティクスを追加することもできます。ただし、これは、人々が通常DELETEを使用する目的に反する可能性があります(人々は通常、キャッシングヘッダーを使用してキャッシングを制御します)。

それをどうやって進めるかは設計上の決定ですが、これが私の考えている方法です。それは完璧ではありません、そしてこれをすることが最善ではない場合があると確信しています(特に非常に複雑な検索基準のために)。

10
luis.espinal

laravel/php バックエンドを使っているので、私はこのようなものを使う傾向があります。

/resource?filters[status_id]=1&filters[city]=Sydney&page=2&include=relatedResource

PHPは自動的に[]パラメータを配列に変換するので、この例では、フィルタの配列/オブジェクトを保持する$filter変数と、ページおよび必要な関連リソースをロードします。

他の言語を使用している場合、これはまだ良い慣習であり、[]を配列に変換するためのパーサーを作成することができます。

8
the-a-train

FYI:私はこれが少し遅れているのを知っています、しかし興味がある人のために。どの程度のRESTfulになりたいかにもよりますが、HTTPの仕様はあまり明確ではないため、独自のフィルタリング戦略を実装する必要があります。すべてのフィルタパラメータをURLエンコードすることをお勧めします。

GET api/users?filter=param1%3Dvalue1%26param2%3Dvalue2

私はそれが醜いことを知っていますが、私はそれがそれをするための最もRESTfulな方法であると思います、そしてサーバー側でパースするのは簡単であるべきです:)

2
shanks