私のアプリケーションには/foo
にリソースがあります。通常、これは次のようなHTTP応答ペイロードによって表されます。
{"a": "some text", "b": "some text", "c": "some text", "d": "some text"}
クライアントは、このオブジェクトの4つのメンバーすべてを常に必要とするわけではありません。 クライアントがサーバーで表現に必要なものをサーバーに伝えるための(== --- ==)RESTfully semantic方法は何ですか?例えば必要な場合:
{"a": "some text", "b": "some text", "d": "some text"}
それはどのようにGET
すべきですか?いくつかの可能性(RESTを誤解している場合は修正を探しています):
GET /foo?sections=a,b,d
。GET /foo/a+b+d
私のお気に入りif REST=セマンティクスはこの問題をカバーしていません。その単純さのためです。/widgets
リソースの提示可能なリストを表す/widget/<id>
と一貫しているため、議論の余地があります。GET /foo/a
などに応答し、クライアントが/foo
のコンポーネントごとに要求するように要求します。/foo
に数百のコンポーネントがあり、クライアントがそれらの100を必要とする場合、悪夢になる可能性があります。/foo
のHTML表現をサポートしたい場合は、Ajaxを使用する必要があります。これは、最小限のブラウザーなどでクロール、レンダリングできる単一のHTMLページだけが必要な場合に問題になります。/foo
:{"a": {"url": "/foo/a", "content": "some text"}, ...}
の他の表現内に存在する必要もあります。GET /foo
、Content-Type: application/json
および{"sections": ["a","b","d"]}
をリクエスト本文に含めます。GET
の本文のセマンティクスを定義していません。正当なHTTPですが、一部のユーザーのプロキシがGET
リクエストから本文を削除しないことをどのように保証できますか?GET
リクエストに本文を入れさせないので、それをテストに使用することはできません。Sections-Needed: a,b,d
POST /foo/requests
、Content-Type: application/json
および{"sections": ["a","b","d"]}
をリクエスト本文に含めます。 201
付きのLocation: /foo/requests/1
を受け取ります。次に、GET /foo/requests/1
を使用して、/foo
の必要な表現を受け取ります。/foo/requests/1
は、1回だけ使用され、要求されるまで保持されるだけのエイリアスであるため、ブックマーク不可およびキャッシュ不可。私は次のことに決めました:
いくつかのメンバーの組み合わせをサポートしています:各組み合わせの名前を付けます。例えば記事に著者、日付、本文のメンバーが含まれている場合、/article/some-slug
はそのすべてを返し、/article/some-slug/meta
は著者と日付のみを返します。
多くの組み合わせのサポート:メンバー名をハイフンで区切ります:/foo/a-b-c
。
どちらの方法でも、組み合わせがサポートされていない場合は404
を返します。
定義から RESTの:
リソース R 時間的に変化するメンバーシップ関数です MR(t)、それはしばらくの間 t 同等のエンティティまたは値のセットにマップします。セット内の値は、リソース表現および/またはリソース識別子である場合があります。
HTTP本文である表現とURLである識別子。
これは非常に重要です。識別子は、他の識別子と表現に関連付けられた単なる値です。これは、識別子→表現のマッピングとは異なります。サーバーは、両方が同じリソースによって関連付けられている限り、任意の識別子を任意の表現にマップできます。
「ユーザー」や「投稿」などのカテゴリを考えることによってビジネスを合理的に説明するリソース定義を思いつくのは開発者次第です。
完全なHATEOASに本当に関心がある場合は、/foo
表現のどこかに/foo/members
へのハイパーリンクを配置できます。その表現には、サポートされているすべてのメンバーの組み合わせへのハイパーリンクが含まれます。
URLの definition から:
クエリコンポーネントには、非階層データが含まれています。これは、パスコンポーネントのデータとともに、URIのスキームと命名機関(存在する場合)のスコープ内でリソースを識別するのに役立ちます。
したがって、/foo?sections=a,b,d
と/foo?sections=b
は別個の識別子です。しかし、それらは、同じリソース内で関連付けられている一方で、異なる表現にマッピングされていることができます。
HTTPの404
コード 手段 サーバーがURLをマップするものを見つけられなかったこと、URLがどのリソースにも関連付けられていないことではありません。
ブラウザやキャッシュでスラッシュやハイフンが問題になることはありません。
クエリ文字列ソリューション(最初の)をお勧めします。他の選択肢に対するあなたの議論は良い議論です(そして私が同じ問題を解決しようとするときに実際に遭遇したものです)。特に、「制約を緩める/ foo/a
に応答する」ソリューションcanは限られたケースで機能しますが、実装と消費の両方からAPIに多くの複雑さをもたらし、そうではありませんでした。私の経験では、努力する価値がありました。
一般的な例を使用して、「意味があるように思われる」引数に弱く対抗します。オブジェクトの大きなリストであるリソース(GET /Customers
)について考えます。これらのオブジェクトをページングすることは完全に合理的であり、クエリ文字列を使用してそれを行うのは一般的です:例としてGET /Customers?offset=100&take=50
。この場合、クエリ文字列は、リストされたオブジェクトのプロパティをフィルタリングしていません。オブジェクトのサブビューのパラメーターを提供しています。
より具体的には、クエリ文字列を使用するためのこれらの基準により、一貫性とHATEOASを維持できると思います。
ただし、これらのUrisのreturnの対象は、より複雑な質問になる場合があります。
/foo
はエンティティですが、foo/a
は文字列です)。代替手段は、部分的に入力されたエンティティを返すことです/foo
にa
がない場合、404
ステータスは誤解を招く(/foo
does存在します!)、しかし空の応答は同様に混乱するかもしれませんa
が必須であるが、クライアントがb
のみを要求する場合、a
、または無効なオブジェクト)これまで、必要なエンティティの特定の名前付き「ビュー」を定義し、?view=summary
や?view=totalsOnly
などのクエリ文字列を許可することで、これを解決しようとしました-順列の数を制限しました。これにより、サービスのコンシューマーにとって「理にかなっている」エンティティーのサブセットの定義が可能になり、文書化することができます。
結局のところ、これは何よりも一貫性の問題に帰着すると思います。クエリ文字列を使用してHATEOASガイダンスに比較的簡単に対応できますが、行う選択はAPI全体で一貫している必要があり、十分に文書化されています。
実際には、リソースの機能によって異なります。たとえば、リソースがエンティティを表す場合:
/customers/5
ここで、「5」は顧客のidを表します
応答:
{
"id": 5,
"name": "John",
"surename": "Doe",
"marital_status": "single",
"sex": "male",
...
}
したがって、詳しく調べると、各jsonプロパティは実際には顧客リソースインスタンスのレコードのfieldを表します。消費者がfieldsの一部である部分的な応答を取得したいとします。消費者がリクエストを通じてさまざまなフィールドを選択する機能を望んでいるので、それを見ることができます。これは、彼にとって興味深いが、それ以上ではありません(フィールドの一部が計算が難しい場合、トラフィックまたはパフォーマンスを節約するため)。 。
この状況では、最も読みやすく正しいAPIは(たとえば、nameおよびsurenameのみを取得する)だと思います)
/customers/5?fields=name,surename
応答:
{
"name": "John",
"surename": "Doe"
}
fields=id,name
またはfields=name,id
)、レスポンスは同じですが、それらのレスポンスは個別にキャッシュされます。リクエストヘッダーapplication/vnd.com.mycompany.resource.rep2で2番目のベンダーのメディアタイプを使用できますが、これをブックマークすることはできませんが、クエリパラメータはキャッシュできません(/ foo?sections = a、b、c )あなたはマトリックスパラメーターを見ることができますが、この質問に関してはそれらはキャッシュ可能でなければなりません RLマトリックスパラメーターvs.リクエストパラメーター
A、b、cがロールプロパティの管理者のようなリソースのプロパティである場合、正しい方法はGET /foo?sections=a,b,d
を提案した最初の方法です。この場合、foo
コレクション。それ以外の場合、a、bおよびcがfoo
コレクションの単一リソースである場合、その後の方法は、一連のGET
要求/foo/a /foo/b /foo/c
を実行することです。このアプローチは、あなたが言ったように、リクエストのペイロードが高いですが、Restfullアプローチに従う正しい方法です。 URLのプラス文字には特別な意味があるため、私はあなたが作成した2番目の提案を使用しません。
もう1つの提案は、GETとPOSTの使用を中止し、foo
コレクションに次のようなアクションを作成することです:/foo/filter
または/foo/selection
または表す動詞コレクションに対するアクション。このように、postリクエスト本文を使用して、リソースのjsonリストを渡すことができます。