Quelle est la meilleure méthode RESTful pour renvoyer le nombre total d’éléments dans un object?

Je développe un service API REST pour un grand site de réseautage social auquel je participe. Jusqu’à présent, cela fonctionne très bien. Je peux émettre des requêtes GET , POST , PUT et DELETE pour les URL d’object et affecter mes données. Cependant, ces données sont paginées (limitées à 30 résultats à la fois).

Cependant, quel serait le meilleur moyen de récupérer le nombre total de membres, via mon API?

Actuellement, je lance des requêtes vers une structure d’URL comme celle-ci:

  • / api / members – Retourne une liste de membres (30 à la fois comme mentionné ci-dessus)
  • / api / members / 1 – Affecte un seul membre, selon 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? Evidemment, demander uniquement le champ id (similaire à l’API graphique de Facebook) et compter les résultats serait inefficace étant donné que seulement une tranche de 30 résultats ne serait renvoyée.

Bien que la réponse à / API / users ne soit que paginée et ne renvoie que 30 enregistrements, rien ne vous empêche d’inclure dans la réponse le nombre total d’enregistrements et d’autres informations pertinentes, telles que la taille de la page, le numéro de page / décalage, 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

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 X-total-count tête X-total-count .
Pour les liens vers la prochaine page, etc., j’utilise l’en-tête http Link :
http://www.w3.org/wiki/LinkHeader

Github fait la même chose: https://developer.github.com/v3/#pagination

À mon avis, c’est plus propre car il peut être utilisé également lorsque vous retournez du contenu qui ne prend pas en charge les liens hypertextes (c.-à-d. Binaires, images).

J’ai fait des recherches approfondies sur cette question et sur d’autres questions liées à la radiomessagerie REST ces derniers temps et j’ai trouvé utile d’append certaines de mes conclusions ici. J’élargis un peu la question pour y inclure des reflections sur la pagination ainsi que sur le compte car elles sont intimement liées.

Les entê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 de réponse elle-même est juste le demandeur de données réel demandé. Rendre le traitement de la réponse plus facile pour les clients qui ne sont pas intéressés par les informations de pagination.

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

X-Total-Count

 X-Total-Count: 234 

Ceci est utilisé dans certaines API que j’ai trouvées dans la nature. Il existe également des packages NPM permettant d’append le support de cet en-tête, par exemple Loopback. Certains articles recommandent également de définir cet en-tête.

Il est souvent utilisé en combinaison avec l’en-tête Link , ce qui est une très bonne solution pour la pagination, mais ne contient pas les informations sur le nombre total.

Lien

 Link: ; rel="previous"; title*=UTF-8'de'letztes%20Kapitel, ; rel="next"; title*=UTF-8'de'n%c3%a4chstes%20Kapitel 

J’ai l’impression, en lisant beaucoup sur ce sujet, que le consensus général est d’utiliser l’en- tête Link pour fournir des liens de pagination aux clients en utilisant rel=next , rel=previous etc. Le problème est qu’il manque d’informations sur le nombre nombre total d’enregistrements, ce qui explique pourquoi de nombreuses API associent cette fonction à l’en X-Total-Count tête X-Total-Count .

Alternativement, certaines API et par exemple le standard JsonApi , utilisent le format Link , mais ajoutent les informations dans une enveloppe de réponse au lieu d’un en-tête. Cela simplifie l’access aux métadonnées (et crée un espace pour append le nombre total d’informations) au désortingment de la complexité croissante de l’access aux données proprement dites (en ajoutant une enveloppe).

Gamme de contenu

 Content-Range: items 0-49/234 

Promu par un article de blog nommé Range header, je vous choisis (pour la pagination)! . L’auteur plaide avec force pour l’utilisation des en Content-Range têtes Range et Content-Range pour la pagination. Lorsque nous lisons attentivement la RFC sur ces en-têtes, nous trouvons que l’extension de leur signification au-delà des plages d’octets était réellement prévue par la RFC et est explicitement autorisée. Lorsqu’il est utilisé dans le contexte d’ items au lieu d’ bytes , l’en-tête Range nous permet de demander une certaine plage d’éléments et d’indiquer la plage du résultat total auquel les éléments de réponse se rapportent. Cet en-tête donne également un excellent moyen de montrer le nombre total. Et c’est un véritable standard qui cartographie principalement en tête-à-tête. Il est également utilisé dans la nature .

Enveloppe

De nombreuses API, y compris celle de notre site Web préféré, utilisent une enveloppe , une enveloppe autour des données utilisées pour append des méta-informations sur les données. De plus, les normes OData et JsonApi utilisent toutes deux une enveloppe de réponse.

Le gros inconvénient de ceci (à mon humble avis) 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. Il y a aussi beaucoup de 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 très différentes, avec un mélange d’OData dans les métadonnées à plusieurs points de la réponse.

Point final séparé

Je pense que cela a été suffisamment couvert dans les autres réponses. Je n’ai pas fait d’enquête car je suis d’accord avec les commentaires selon lesquels cela est source de confusion car vous avez maintenant plusieurs types de points de terminaison. Je pense que c’est plus agréable si chaque point d’extrémité représente une (collection de) ressource (s).

Pensées supplémentaires

Nous ne devons pas seulement communiquer les méta-informations de pagination liées à la réponse, mais également 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 parameters de requête. Certains préconisent de traiter les pages de résultats comme des ressources distinctes, ce qui peut être utile dans certains cas d’utilisation (par exemple, /books/231/pages/52 J’ai fini par sélectionner une plage de parameters fréquemment utilisés tels que pagesize , page[size] et limit en plus de supporter l’en-tête Range (et aussi comme paramètre de requête).

Vous pouvez renvoyer le nombre en tant qu’en-tête HTTP personnalisé en réponse à une demande HEAD. De cette façon, si un client ne veut que le nombre, vous n’avez pas besoin de retourner la liste réelle, et vous n’avez pas besoin d’une URL supplémentaire.

(Ou, si vous vous trouvez dans un environnement contrôlé, d’un point d’extrémité à un autre, vous pouvez utiliser un verbe HTTP personnalisé tel que COUNT.)

Alternative lorsque vous n’avez pas besoin d’éléments réels

La réponse de Franci Penov est certainement la meilleure solution pour que vous retourniez toujours les articles avec toutes les métadonnées supplémentaires sur vos entités demandées. C’est comme ça que ça devrait être fait.

mais parfois, le retour de toutes les données n’a pas de sens, car vous n’en avez peut-être pas besoin du tout. Vous n’avez peut-être besoin que de métadonnées sur votre ressource demandée. Comme le nombre total ou le nombre de pages ou autre chose. Dans un tel cas, vous pouvez toujours demander à votre service de ne pas renvoyer des éléments, mais plutôt des métadonnées comme:

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

ou quelque chose de similaire …

Je recommanderais d’append des en-têtes identiques, comme:

 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" } ] 

Pour plus de détails se référer à:

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

Pour le fichier swagger:

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

Qu’en est-il d’un nouveau noeud final> / api / members / count qui appelle simplement Members.Count () et renvoie le résultat

Semble plus facile d’append simplement un

 GET /api/members/count 

et renvoyer le nombre total de membres

Parfois, les frameworks (comme $ resource / AngularJS) nécessitent un tableau comme résultat de requête, et vous ne pouvez pas vraiment avoir de réponse comme {count:10,items:[...]} dans ce cas, je stocke “count” dans responseHeaders .

PS En fait, vous pouvez le faire avec $ resource / AngularJS, mais cela nécessite quelques modifications.

Lorsque vous demandez des données paginées, vous connaissez (par la valeur du paramètre de taille de page explicite ou la valeur de taille de page par défaut) la taille de la page, de sorte que vous sachiez si vous avez reçu toutes les données. Lorsqu’il y a moins de données en réponse que la taille d’une page, vous obtenez alors des données complètes. Lorsqu’une page complète est renvoyée, vous devez redemander une autre page.

Je préfère avoir un sharepoint terminaison séparé pour le comptage (ou le même point final avec le paramètre countOnly). Parce que vous pouvez préparer l’utilisateur final à un processus long / long en affichant la barre de progression correctement lancée.

Si vous souhaitez renvoyer des données dans chaque réponse, il devrait y avoir une taille de page, une mention de décalage également. Pour être honnête, le meilleur moyen est de répéter les filtres de demande. Mais la réponse est devenue très complexe. Je préfère donc un point final dédié au nombre de retours.

              

Couleage de la mienne, préférer un paramètre countOnly au point final existant. Ainsi, lorsque spécifié, la réponse contient uniquement des métadonnées.

point final? filtre = valeur

     ...   

sharepoint terminaison? filter = valeur & countOnly = true