190 votes

Quelle est la meilleure méthode RESTful pour retourner le nombre total d'éléments dans un objet ?

Je développe un service API REST pour un grand site de réseautage social dans lequel je suis impliqué. Jusqu'à présent, cela fonctionne très bien. Je peux émettre des GET , POST , PUT y DELETE à des URL d'objets et affectent mes données. Cependant, ces données sont paginées (limitées à 30 résultats à la fois).

Quel serait le meilleur moyen RESTful d'obtenir le nombre total de membres, par exemple, via mon API ?

Actuellement, j'envoie des requêtes à une structure URL comme la suivante :

  • /api/membres - Renvoie une liste de membres (30 à la fois comme indiqué ci-dessus)
  • /api/membres/1 - Affecte un seul membre, en fonction de la méthode de requête utilisée

Ma question est la suivante : comment puis-je utiliser une structure URL similaire pour obtenir le nombre total de membres dans mon application ? Il est évident qu'en demandant seulement le id (similaire à l'API Graph de Facebook) et le comptage des résultats serait inefficace car seule une tranche de 30 résultats serait renvoyée.

114voto

Stijn de Witt Points 3515

J'ai fait des recherches approfondies sur cette question et sur d'autres questions relatives à la pagination REST ces derniers temps et j'ai pensé qu'il était constructif d'ajouter certaines de mes conclusions ici. J'élargis un peu la question pour inclure des réflexions sur la pagination ainsi que sur le comptage car ils sont intimement liés.

En-têtes

Les métadonnées de pagination sont incluses dans la réponse sous la forme d'en-têtes de réponse. Le grand avantage de cette approche est que la charge utile de la réponse elle-même n'est que les données réelles demandées par le demandeur. Cela facilite le traitement de la réponse pour les clients qui ne sont pas intéressés par les informations de pagination.

Il existe un certain nombre d'en-têtes (standard et personnalisés) utilisés dans la nature pour renvoyer des informations relatives à la pagination, y compris le décompte total.

X-Total-Count

X-Total-Count: 234

Il est utilisé dans algunos API Je l'ai trouvé dans la nature. Il y a aussi Paquets NPM pour ajouter la prise en charge de cet en-tête à Loopback, par exemple. Quelques articles recommande également de paramétrer cet en-tête.

Il est souvent utilisé en combinaison avec le Link qui est une bonne solution pour la pagination, mais qui ne contient pas d'informations sur le nombre total.

Lien

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

J'ai l'impression, après avoir lu beaucoup de choses sur ce sujet, que le consensus général est d'utiliser l'outil Link en-tête pour fournir des liens de radiomessagerie aux clients qui utilisent rel=next , rel=previous etc. Le problème est qu'il manque l'information sur le nombre total d'enregistrements, c'est pourquoi de nombreuses API la combinent avec l'option X-Total-Count l'en-tête.

Par ailleurs, certaines API, par exemple l'API JsonApi utiliser la norme Link mais en ajoutant les informations dans une enveloppe de réponse plutôt que dans un en-tête. Cela simplifie l'accès aux métadonnées (et crée un endroit où ajouter l'information sur le nombre total) au détriment de la complexité accrue de l'accès aux données elles-mêmes (par l'ajout d'une enveloppe).

Plage de contenu

Content-Range: items 0-49/234

Promu par un article de blog intitulé Range header, je te choisis (pour la pagination) ! . L'auteur présente des arguments solides en faveur de l'utilisation de la Range y Content-Range pour la pagination. Lorsque nous lisons attentivement les RFC sur ces en-têtes, nous constatons que l'extension de leur signification au-delà des plages d'octets était en fait prévue par le RFC et est explicitement autorisée. Lorsqu'ils sont utilisés dans le contexte de items au lieu de bytes L'en-tête Range nous permet de demander un certain nombre d'éléments et d'indiquer à quelle partie du résultat total les éléments de la réponse se rapportent. Cet en-tête constitue également un excellent moyen d'afficher le nombre total d'éléments. Il s'agit en outre d'une véritable norme qui correspond en grande partie à la pagination. C'est aussi utilisés dans la nature .

Enveloppe

De nombreuses API, y compris celui de notre site de questions-réponses préféré utiliser un enveloppe Les données sont entourées d'une enveloppe qui permet d'ajouter des méta-informations sur les données. Aussi, OData y JsonApi les normes utilisent toutes deux une enveloppe de réponse.

Le gros inconvénient de cette méthode (imho) est que le traitement des données de réponse devient plus complexe car les données réelles doivent être trouvées quelque part dans l'enveloppe. De plus, il existe de nombreux formats différents pour cette enveloppe et vous devez utiliser le bon. Il est révélateur que les enveloppes de réponse d'OData et de JsonApi soient extrêmement différentes, OData mélangeant des métadonnées à plusieurs endroits de la réponse.

Point final séparé

Je pense que cette question a été suffisamment traitée dans les autres réponses. Je n'ai pas approfondi cette question car je suis d'accord avec les commentaires selon lesquels cela prête à confusion car vous avez maintenant plusieurs types de points de terminaison. Je pense qu'il est préférable que chaque point de terminaison représente une (collection de) ressource(s).

Réflexions complémentaires

Nous devons non seulement communiquer les méta-informations relatives à la réponse, mais aussi permettre au client de demander des pages/plages spécifiques. Il est intéressant d'examiner également cet aspect pour aboutir à une solution cohérente. Ici aussi, nous pouvons utiliser des en-têtes (l'en-tête Range semble très approprié), ou d'autres mécanismes tels que les paramètres de requête. Certains préconisent de traiter les pages de résultats comme des ressources distinctes, ce qui peut avoir du sens dans certains cas d'utilisation (par ex. /books/231/pages/52 . J'ai fini par sélectionner un large éventail de paramètres de requête fréquemment utilisés, tels que pagesize , page[size] y limit etc. en plus de soutenir la Range (et également comme paramètre de la requête).

100voto

Franci Penov Points 45358

Bien que la réponse à /API/users soit paginée et ne renvoie que 30 enregistrements, rien ne vous empêche d'inclure également dans la réponse le nombre total d'enregistrements et d'autres informations pertinentes, telles que la taille de la page, le numéro/le décalage de la page, etc.

L'API StackOverflow est un bon exemple de cette même conception. Voici la documentation de la méthode Users - https://api.stackexchange.com/docs/users

86voto

Ondrej Bozek Points 1988

Je préfère utiliser les en-têtes HTTP pour ce type d'informations contextuelles.

Pour le nombre total d'éléments, j'utilise la fonction X-total-count l'en-tête.
Pour les liens vers la page suivante, précédente, etc. j'utilise HTTP Link l'en-tête :
http://www.w3.org/wiki/LinkHeader

Github procède de la même manière : https://docs.github.com/en/rest/overview/resources-in-the-rest-api#pagination

À mon avis, c'est plus propre puisqu'il peut être utilisé également lorsque vous renvoyez un contenu qui ne supporte pas les liens hypertextes (par exemple, des binaires, des images).

30voto

Robert Koritnik Points 45499

Alternative lorsque vous n'avez pas besoin d'articles réels

Réponse de Franci Penov est certainement la meilleure façon de procéder afin de toujours renvoyer les éléments avec toutes les métadonnées supplémentaires concernant les entités demandées. C'est ainsi qu'il faut procéder.

mais il n'est pas toujours utile de renvoyer toutes les données, car vous n'en avez peut-être pas besoin du tout. Il se peut que vous n'ayez besoin que des métadonnées relatives à la ressource demandée. Comme le nombre total ou le nombre de pages ou quelque chose d'autre. Dans ce cas, vous pouvez toujours demander à l'URL de ne pas renvoyer d'éléments, mais seulement des métadonnées, par exemple :

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

ou quelque chose de similaire...

23voto

bzlm Points 5500

Vous pouvez renvoyer le décompte sous la forme d'un en-tête HTTP personnalisé en réponse à une requête HEAD. De cette façon, si un client ne veut que le décompte, vous n'avez pas besoin de renvoyer la liste proprement dite, et il n'est pas nécessaire d'ajouter une URL supplémentaire.

(Ou, si vous êtes dans un environnement contrôlé d'un point final à l'autre, vous pouvez utiliser un verbe HTTP personnalisé tel que COUNT).

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X