私は現在、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¶meter2=value2¶meter3=value3¶meter4=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ではないようです。
RESTful検索を実装する最良の方法は、検索自体をリソースと見なすことです。検索を作成しているので、その後、POST動詞を使用できます。 POSTを使用するために文字通りデータベースに何かを作成する必要はありません。
例えば:
Accept: application/json
Content-Type: application/json
POST http://example.com/people/searches
{
"terms": {
"ssn": "123456789"
},
"order": { ... },
...
}
ユーザーの観点から検索を作成しています。この実装の詳細は関係ありません。いくつかのRESTful APIは永続性さえ必要としないかもしれません。それが実装の詳細です。
GETリクエストでリクエストボディを使用すると、キャッシュシステムがURLのみを使用するため、GETリクエストをキャッシュできないため、RESTの原則に違反します。
さらに悪いことに、あなたのURLはブックマークできません。URLにはユーザーをこのページにリダイレクトするのに必要なすべての情報が含まれていないからです。
リクエストボディパラメータの代わりにURLまたはクエリパラメータを使用してください。
例えば。:
/myapp?var1=xxxx&var2=xxxx
/myapp;var1=xxxx/resource;var2=xxxx
実際、HTTP RFC 7231には次のように記載されています。
GETリクエストメッセージ内のペイロードには定義済みのセマンティクスがありません。 GETリクエストでペイロードボディを送信すると、一部の既存の実装でリクエストが拒否される可能性があります。
詳細については こちら をご覧ください。
リソースのフィルタリング/検索はRESTfulな方法で実装できるようです。アイデアは、/filters/
または/api/filters/
と呼ばれる新しいエンドポイントを導入することです。
このエンドポイントを使用する filter はリソースと見なすことができ、したがってPOST
メソッドで作成できます。このように - もちろん - bodyを使ってすべてのパラメータを運ぶことができ、複雑な検索/フィルタ構造を作成することもできます。
このようなフィルタを作成した後、検索/フィルタ結果を取得するには2つの方法があります。
一意のIDを持つ新しいリソースが201 Created
ステータスコードと共に返されます。それから、このIDを使ってGET
リクエストを/api/users/
に対して行うことができます。
GET /api/users/?filterId=1234-abcd
POST
を介して新しいフィルタが作成された後は、201 Created
で応答しませんが、303 SeeOther
を指すLocation
ヘッダと同時に/api/users/?filterId=1234-abcd
で応答します。このリダイレクトは、基盤となるライブラリを介して自動的に処理されます。
どちらのシナリオでも、フィルタリングされた結果を取得するために2つの要求を行う必要があります。これは、特にモバイルアプリケーションの場合、欠点と見なされる可能性があります。モバイルアプリケーションのために私は/api/users/filter/
への単一のPOST
呼び出しを使用するでしょう。
作成したフィルタを保持する方法
それらはDBに格納して後で使用することができます。それらはまた、いくつかの一時的な記憶装置に格納することもできる。再表示していくつかのTTLを指定すると、期限切れになり削除されます。
このアイデアの利点は何ですか?
フィルタ、フィルタ処理された結果はキャッシュ可能で、ブックマークさえもできます。
初期の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を使用する目的に反する可能性があります(人々は通常、キャッシングヘッダーを使用してキャッシングを制御します)。
それをどうやって進めるかは設計上の決定ですが、これが私の考えている方法です。それは完璧ではありません、そしてこれをすることが最善ではない場合があると確信しています(特に非常に複雑な検索基準のために)。
laravel/php バックエンドを使っているので、私はこのようなものを使う傾向があります。
/resource?filters[status_id]=1&filters[city]=Sydney&page=2&include=relatedResource
PHPは自動的に[]
パラメータを配列に変換するので、この例では、フィルタの配列/オブジェクトを保持する$filter
変数と、ページおよび必要な関連リソースをロードします。
他の言語を使用している場合、これはまだ良い慣習であり、[]
を配列に変換するためのパーサーを作成することができます。
FYI:私はこれが少し遅れているのを知っています、しかし興味がある人のために。どの程度のRESTfulになりたいかにもよりますが、HTTPの仕様はあまり明確ではないため、独自のフィルタリング戦略を実装する必要があります。すべてのフィルタパラメータをURLエンコードすることをお勧めします。
GET api/users?filter=param1%3Dvalue1%26param2%3Dvalue2
私はそれが醜いことを知っていますが、私はそれがそれをするための最もRESTfulな方法であると思います、そしてサーバー側でパースするのは簡単であるべきです:)