Je développe un service d'API REST pour un grand site Web de réseautage social auquel je participe. Jusqu'à présent, cela fonctionne très bien. Je peux émettre GET
, POST
, PUT
et DELETE
demande aux URL de l' objet 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 RESTful d'obtenir le nombre total de membres, disons, via mon API?
Actuellement, j'émets des demandes à une structure d'URL comme la suivante:
- / api / members: renvoie 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 d'URL similaire pour obtenir le nombre total de membres dans mon application? De toute évidence, ne demander que le id
champ (similaire à l'API Graph de Facebook) et compter les résultats serait inefficace étant donné que seule une tranche de 30 résultats ne serait renvoyée.
la source
Réponses:
Alors que la réponse à / API / users est paginée et ne renvoie que 30 enregistrements, rien ne vous empêche d'inclure dans la réponse également le nombre total d'enregistrements et d'autres informations pertinentes, telles que la taille de la page, le numéro de page / le 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
la source
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 l'en-
X-total-count
tête.Pour les liens vers la page suivante, précédente, etc. J'utilise l'en-
Link
tête http :http://www.w3.org/wiki/LinkHeader
Github le fait de la même manière: https://developer.github.com/v3/#pagination
À mon avis, c'est plus propre car il peut également être utilisé lorsque vous renvoyez du contenu qui ne prend pas en charge les hyperliens (c'est-à-dire les binaires, les images).
la source
X-
.J'ai récemment fait des recherches approfondies sur cette question et sur d'autres questions liées à la pagination REST 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 décompte car elles sont intimement liées.
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 la demande de données réelle. Faciliter le traitement de la réponse pour les clients qui ne sont pas intéressés par les informations de pagination.
Il existe de nombreux en-têtes (standard et personnalisés) utilisés à l'état sauvage pour renvoyer des informations relatives à la pagination, y compris le nombre total.
Nombre total X
Ceci est utilisé dans certaines API que j'ai trouvées dans la nature. Il existe également des packages NPM pour ajouter la prise en charge 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-
Link
tête, qui est une très bonne solution pour la pagination, mais ne dispose pas des informations de comptage total.Lien
J'ai le sentiment, après avoir lu beaucoup sur ce sujet, que le consensus général est d'utiliser l'en-
Link
tête pour fournir des liens de pagination aux clients utilisantrel=next
,rel=previous
etc. Le problème avec cela est qu'il manque d'informations sur le nombre total d'enregistrements, ce qui pourquoi de nombreuses API combinent cela avec l'en-X-Total-Count
tête.Alternativement, certaines API et par exemple la norme JsonApi , utilisent le
Link
format, mais ajoutent les informations dans une enveloppe de réponse au lieu de dans un en-tête. Cela simplifie l'accès aux métadonnées (et crée un emplacement pour ajouter les informations de comptage total) au détriment de la complexité croissante de l'accès aux données réelles elles-mêmes (en ajoutant une enveloppe).Gamme de contenu
Promu par un article de blog nommé Range header, je vous choisis (pour la pagination)! . L'auteur plaide fermement en faveur de l'utilisation des en
Range
-Content-Range
têtes et pour la pagination. Lorsque nous lisons attentivement la RFC sur ces en-têtes, nous constatons que l'extension de leur signification au-delà des plages d'octets était en fait anticipée par la RFC et est explicitement autorisée. Lorsqu'il est utilisé dans le contexte deitems
plutôt que debytes
, l'en-tête Range nous donne en fait un moyen de demander une certaine plage d'éléments et d'indiquer à quelle plage du résultat total les éléments de réponse se rapportent. Cet en-tête offre également un excellent moyen d'afficher le nombre total. Et c'est un vrai standard qui mappe principalement un à un à la pagination. Il est également utilisé dans la nature .Enveloppe
De nombreuses API, y compris celle de notre site Web de questions-réponses préféré, utilisent une enveloppe , un wrapper autour des données qui est utilisé pour ajouter 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 cela (à 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 existe également 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 sont extrêmement différentes, OData se mélangeant dans les métadonnées à plusieurs points de la réponse.
Point de terminaison séparé
Je pense que cela a été suffisamment couvert dans les autres réponses. Je n'ai pas beaucoup enquêté 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 que c'est plus agréable si chaque point de terminaison représente une (collection de) ressource (s).
Réflexions supplémentaires
Nous devons non seulement communiquer les méta-informations de pagination liées à la réponse, mais aussi permettre au client de demander des pages / plages spécifiques. Il est intéressant de se pencher également sur cet aspect pour aboutir à une solution cohérente. Ici aussi, nous pouvons utiliser des en-têtes (l'en-
Range
tête semble très approprié), ou d'autres mécanismes tels que les paramètres de requête. Certaines personnes préconisent le traitement des pages de résultats comme des ressources distinctes, ce qui peut donner un sens dans certains cas d'utilisation (par exemple/books/231/pages/52
. J'ai fini par sélectionner une plage sauvage de paramètres de demande fréquemment utilisés tels quepagesize
,page[size]
etlimit
etc , en plus de soutenir l' en-Range
tête (et comme paramètre de requête ainsi que).la source
Range
tête, mais je n'ai pas pu trouver suffisamment de preuves que l'utilisation de tout autre élément quebytes
comme type de plage est valide.acceptable-ranges = 1#range-unit | "none"
je pense que cette formulation laisse explicitement de la place pour d'autres unités de plage quebytes
, bien que la spécification elle-même ne fasse que définirbytes
.Alternative lorsque vous n'avez pas besoin d'articles réels
La réponse de Franci Penov est certainement la meilleure façon de procéder afin que vous retourniez toujours des éléments avec toutes les métadonnées supplémentaires sur vos entités demandées. C'est ainsi que cela devrait être fait.
mais parfois, renvoyer toutes les données n'a pas de sens, car vous n'en avez peut-être pas du tout besoin. Peut-être que tout ce dont vous avez besoin, ce sont des métadonnées sur la ressource demandée. Comme le nombre total ou le nombre de pages ou autre chose. Dans ce cas, vous pouvez toujours demander à votre service de demander à votre service de renvoyer des éléments, mais plutôt des métadonnées comme:
ou quelque chose de similaire ...
la source
metaonly
ouincludeitems
non.Vous pouvez renvoyer le décompte sous forme d'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 réelle et il n'est pas nécessaire d'avoir une URL supplémentaire.
(Ou, si vous êtes dans un environnement contrôlé d'un point de terminaison à un autre, vous pouvez utiliser un verbe HTTP personnalisé tel que COUNT.)
la source
Je recommanderais d'ajouter des en-têtes pour le même, comme:
Pour plus de détails, reportez-vous à:
https://github.com/adnan-kamili/rest-api-response-format
Pour le fichier swagger:
https://github.com/adnan-kamili/swagger-response-template
la source
Depuis "X -" - Le préfixe est obsolète. (voir: https://tools.ietf.org/html/rfc6648 )
Nous avons trouvé les "Accept-Ranges" comme étant le meilleur pari pour mapper la pagination allant: https://tools.ietf.org/html/rfc7233#section-2.3 Car les "Range Units" peuvent être soit "bytes", soit " jeton". Les deux ne représentent pas un type de données personnalisé. (voir: https://tools.ietf.org/html/rfc7233#section-4.2 ) Pourtant, il est indiqué que
Ce qui indique: l'utilisation d'unités de plage personnalisées n'est pas contraire au protocole, mais elle PEUT être ignorée.
De cette façon, nous devrions définir les plages d'acceptation sur "membres" ou quel que soit le type d'unité à distance, nous nous attendons à ce que cela soit possible. Et en outre, définissez également la plage de contenu sur la plage actuelle. (voir: https://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.12 )
Quoi qu'il en soit, je m'en tiendrai à la recommandation de la RFC7233 ( https://tools.ietf.org/html/rfc7233#page-8 ) d'envoyer un 206 au lieu de 200:
Donc, en conséquence, nous aurions les champs d'en-tête HTTP suivants:
Pour un contenu partiel:
Pour le contenu complet:
la source
Il semble plus facile d'ajouter simplement un
et renvoyer le nombre total de membres
la source
Qu'en est-il d'un nouveau point de terminaison> / api / members / count qui appelle simplement Members.Count () et renvoie le résultat
la source
members
collection peut être créée par une requête POST à/api
, sera-t-elle/api/members/count
également créée comme effet secondaire, ou dois-je faire une requête POST explicite pour la créer avant de la demander? :-)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 ajustements.
la source
isArray: false|true
Vous pourriez considérer
counts
comme une ressource. L'URL serait alors:la source
Lorsque vous demandez des données paginées, vous connaissez (par valeur de paramètre de taille de page explicite ou valeur de taille de page par défaut) la taille de la page, vous savez donc si vous avez toutes les données en réponse ou non. Lorsqu'il y a moins de données en réponse qu'une taille de page, vous obtenez 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 point de terminaison séparé pour le nombre (ou le même point de terminaison avec le paramètre countOnly). Parce que vous pouvez préparer l'utilisateur final à un processus long / fastidieux en affichant la barre de progression correctement lancée.
Si vous souhaitez renvoyer la taille des données dans chaque réponse, il doit y avoir pageSize, offset également mentionné. Pour être honnête, le meilleur moyen est de répéter également une demande de filtres. Mais la réponse est devenue très complexe. Donc, je préfère un point de terminaison dédié pour renvoyer le nombre.
Ma couleur, préférez un paramètre countOnly au point de terminaison existant. Ainsi, lorsqu'elle est spécifiée, la réponse contient uniquement des métadonnées.
endpoint? filter = valeur
endpoint? filter = value & countOnly = true
la source