web-dev-qa-db-ja.com

オブジェクト内のアイテムの合計数を返すための最適なRESTfulメソッドは何ですか?

私は、関与している大規模なソーシャルネットワーキングWebサイト用のREST AP​​Iサービスを開発しています。これまでのところ、うまく機能しています。オブジェクトURLにGETPOSTPUTおよびDELETEリクエストを発行して、データに影響を与えることができます。ただし、このデータはページングされます(一度に30の結果に制限されます)。

しかし、私のAPIを介して、たとえばメンバーの総数を取得するためのRESTfulな最良の方法は何でしょうか?

現在、次のようなURL構造にリクエストを発行しています。

  • / api/members —メンバーのリストを返します(上記のように一度に30)
  • / api/members/1 —使用するリクエスト方法に応じて、単一のメンバーに影響します

私の質問は次のとおりです。次に、アプリケーション内のメンバーの総数を取得するために、同様のURL構造をどのように使用しますか?明らかにidフィールドのみをリクエストし(FacebookのGraph APIと同様)、結果をカウントすることは、30の結果のスライスのみが返されることを考えると無効になります。

123
Martin Bean

/ API/usersへの応答はページ化され、30レコードのみを返しますが、応答にレコードの合計数、およびページサイズ、ページ番号/オフセットなどのその他の関連情報も含めることを妨げるものはありません。

StackOverflow APIは、同じ設計の良い例です。 Usersメソッドのドキュメントを次に示します- https://api.stackexchange.com/docs/users

78
Franci Penov

この種のコンテキスト情報にはHTTPヘッダーを使用することを好みます。

要素の総数には、X-total-countヘッダーを使用します。
次、前のページなどへのリンクには、http Linkヘッダーを使用します。
http://www.w3.org/wiki/LinkHeader

Githubは同じ方法で行います: https://developer.github.com/v3/#pagination

私の意見では、ハイパーリンクをサポートしていないコンテンツ(つまり、バイナリ、写真)を返すときにも使用できるため、よりクリーンです。

62
Ondrej Bozek

私はこの問題やその他のRESTページング関連の質問に最近広範な調査を行ってきましたが、ここに私の調査結果のいくつかを追加することは建設的だと思いました。私は質問を少し広げて、ページングに関する考えと、それらが密接に関連しているためのカウントを含めるようにしています。

ヘッダー

ページングメタデータは、応答ヘッダーの形式で応答に含まれます。このアプローチの大きな利点は、応答ペイロード自体が、要求された実際のデータリクエスターにすぎないことです。ページング情報に関心のないクライアントの応答の処理を簡単にします。

合計数を含むページング関連の情報を返すために、ワイルドで使用される(標準およびカスタム)ヘッダーの束があります。

X合計カウント

X-Total-Count: 234

これは someAPIs で使用されています。このヘッダーのサポートを追加するための NPM packages もあります。ループバック。いくつかの 記事 このヘッダーも設定することをお勧めします。

多くの場合、Linkヘッダーと組み合わせて使用​​されます。これは、ページングには非常に優れたソリューションですが、合計カウント情報が不足しています。

リンク

Link: </TheBook/chapter2>;
      rel="previous"; title*=UTF-8'de'letztes%20Kapitel,
      </TheBook/chapter4>;
      rel="next"; title*=UTF-8'de'n%c3%a4chstes%20Kapitel

このテーマについてよく読んで、一般的なコンセンサスは、 Link header を使用して、rel=nextrel=previousを使用してクライアントにページングリンクを提供することです。これに関する問題は、合計レコード数の情報が不足していることです。これが、多くのAPIがX-Total-Countヘッダーとこれを組み合わせる理由です。

または、一部のAPIおよび JsonApi standard、Link形式を使用しますが、ヘッダーではなく応答エンベロープに情報を追加します。これにより、メタデータへのアクセスが簡素化され(合計カウント情報を追加する場所が作成されます)、実際のデータ自体へのアクセスが複雑になります(エンベロープが追加されます)。

コンテンツ範囲

Content-Range: items 0-49/234

Rangeヘッダー、(ページネーション用)を選択します! という名前のブログ記事によって促進されます。著者は、ページネーションにRangeおよびContent-Rangeヘッダーを使用することを強く主張しています。これらのヘッダーで theRFC を注意深く読むと、バイトの範囲を超えて意味を拡張することが実際にRFCによって予期され、明示的に許可されていることがわかります。 itemsの代わりにbytesのコンテキストで使用される場合、Rangeヘッダーは実際に特定の範囲のアイテムを要求し、応答アイテムが関連する合計結果の範囲を示す方法を提供します。このヘッダーは、総数を表示する優れた方法も提供します。そして、それはほとんどがページングに1対1で対応する真の標準です。また、 野生で使用 です。

封筒

私たちのお気に入りのQ&Aウェブサイトからのもの を含む多くのAPIは、envelopeを使用します。データに関するメタ情報。また、 OData および JsonApi 規格は両方とも応答エンベロープを使用します。

これの大きな欠点(imho)は、実際のデータをエンベロープのどこかに見つける必要があるため、応答データの処理がより複雑になることです。また、そのエンベロープにはさまざまな形式があり、適切な形式を使用する必要があります。 ODataとJsonApiからの応答エンベロープが大きく異なることを示しており、ODataは応答の複数のポイントでメタデータに混合しています。

別のエンドポイント

これは他の回答でも十分にカバーされていると思います。あなたが複数のタイプのエンドポイントを持っているのでこれが混乱しているというコメントに同意するので、私はこれをあまり調査しませんでした。すべてのエンドポイントがリソース(のコレクション)を表す場合、最も良いと思います。

さらなる考え

応答に関連するページングメタ情報を伝えるだけでなく、クライアントが特定のページ/範囲をリクエストできるようにします。一貫性のあるソリューションに至るために、この側面を見るのも興味深いです。ここでも、ヘッダー(Rangeヘッダーが非常に適しているようです)、またはクエリパラメーターなどの他のメカニズムを使用できます。結果のページを個別のリソースとして扱うことを推奨する人もいます。これは、いくつかのユースケースで意味があります(例:/books/231/pages/52pagesizepage[size]およびlimitなどRangeヘッダーのサポートに加えて(および要求パラメーターとしても)。

56
Stijn de Witt

HEADリクエストへの応答で、カスタムHTTPヘッダーとしてカウントを返すことができます。このように、クライアントがカウントのみを必要とする場合、実際のリストを返す必要はなく、追加のURLは不要です。

(または、エンドポイント間で制御された環境にいる場合は、COUNTなどのカスタムHTTP動詞を使用できます。)

22
bzlm

実際のアイテムが不要な場合の代替

Franci Penov's answer は間違いなく最善の方法です。したがって、要求されているエンティティに関するすべての追加メタデータとともに常にアイテムを返します。それはそれが行われるべき方法です。

ただし、すべてのデータを返すことが意味をなさない場合があります。まったく必要ない場合があるためです。必要なのは、リクエストされたリソースに関するメタデータだけかもしれません。総カウントやページ数など。このような場合、URLクエリを使用して、アイテムを返さないようにサービスに指示することができます。

/api/members?metaonly=true
/api/members?includeitems=0

または類似のもの...

21
Robert Koritnik

同じようなヘッダーを追加することをお勧めします:

HTTP/1.1 200

Pagination-Count: 100
Pagination-Page: 5
Pagination-Limit: 20
Content-Type: application/json

[
  {
    "id": 10,
    "name": "shirt",
    "color": "red",
    "price": "$23"
  },
  {
    "id": 11,
    "name": "shirt",
    "color": "blue",
    "price": "$25"
  }
]

詳細については、以下を参照してください。

https://github.com/adnan-kamili/rest-api-response-format

Swaggerファイルの場合:

https://github.com/adnan-kamili/swagger-response-template

11
adnan kamili

"X-"-Prefixは廃止されました。 (参照: https://tools.ietf.org/html/rfc6648

「Accept-Ranges」がページネーションの範囲をマッピングするための最善策であることがわかりました。 https://tools.ietf.org/html/rfc7233#section-2. 「Range Units」は「バイト」または「トークン」のいずれかです。どちらもカス​​タムデータタイプを表しません。 (参照: https://tools.ietf.org/html/rfc7233#section-4.2 )それでも、

HTTP/1.1実装は、他のユニットを使用して指定された範囲を無視する場合があります。

これは、カスタムの範囲単位を使用することはプロトコルに反するものではありませんが、無視してもよいことを示しています。

この方法では、Accept-Rangesを「メンバー」または任意の範囲のユニットタイプに設定する必要があります。さらに、Content-Rangeも現在の範囲に設定します。 (参照: https://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.12

いずれにしても、200の代わりに206を送信するために、RFC7233( https://tools.ietf.org/html/rfc7233#page-8 )の推奨事項に固執します。

すべての前提条件が真の場合、サーバーは範囲をサポートします
ターゲットリソースのヘッダーフィールドで、指定された範囲は
有効かつ充足可能(セクション2.1で定義)、サーバーは
206(部分コンテンツ)応答に1つを含むペイロードを送信します
または充足可能に対応するより部分的な表現
セクション4で定義されている要求範囲。

そのため、結果として、次のHTTPヘッダーフィールドがあります。

部分コンテンツの場合:

206 Partial Content
Accept-Ranges: members
Content-Range: members 0-20/100

完全なコンテンツの場合:

200 OK
Accept-Ranges: members
Content-Range: members 0-20/20
5
Lepidopteron

Members.Count()を呼び出して結果を返すだけの新しいエンドポイント>/api/members/countはどうですか

2
Steve Woods

を追加するのが最も簡単なようです

GET
/api/members/count

メンバーの総数を返します

2

フレームワーク($ resource/AngularJSなど)はクエリ結果として配列を必要とする場合があり、{count:10,items:[...]}のような応答を実際に取得できない場合、responseHeadersに "count"を格納します。

P. S.実際には、$ resource/AngularJSでそれを行うことができますが、いくつかの調整が必要です。

2