web-dev-qa-db-fra.com

Pagination dans une application Web REST)

Ceci est une reformulation plus générique de cette question (avec l'élimination des Rails))

Je ne sais pas comment implémenter la pagination sur une ressource dans une application Web RESTful. En supposant que j’ai une ressource appelée products, quelle est, selon vous, la meilleure approche et pourquoi:

1. Utiliser uniquement des chaînes de requête

par exemple. http://application/products?page=2&sort_by=date&sort_how=asc
Le problème ici est que je ne peux pas utiliser la mise en cache pleine page et que l'URL n'est pas très propre et facile à mémoriser.

2. Utilisation de pages en tant que ressources et chaînes de requête pour le tri

par exemple. http://application/products/page/2?sort_by=date&sort_how=asc
Dans ce cas, le problème que l’on voit est que http://application/products/pages/1 n'est pas une ressource unique car utiliser sort_by=price peut donner un résultat totalement différent et . Je ne peux toujours pas utiliser la mise en cache des pages.

3. Utilisation de pages en tant que ressources et d'un segment d'URL pour le tri

par exemple. http://application/products/by-date/page/2
Personnellement, je ne vois pas de problème à utiliser cette méthode, mais quelqu'un m'a averti que ce n'est pas une bonne façon de faire (il n'a pas donné de raison, alors si vous savez pourquoi ce n'est pas recommandé, merci de me le faire savoir)

Toutes les suggestions , opinions, critiques sont les bienvenues. Merci.

323
andi

Je pense que le problème avec la version 3 est plus un problème de "point de vue" - voyez-vous la page comme la ressource ou les produits sur la page.

Si vous voyez la page comme ressource, c'est une solution parfaite, car la requête pour la page 2 donnera toujours la page 2.

Mais si vous voyez les produits sur la page en tant que ressource, vous avez le problème que les produits de la page 2 pourraient être modifiés (anciens produits supprimés ou autre), dans ce cas, l'URI ne renvoie pas toujours les mêmes ressources.

Par exemple. Un client stocke un lien vers la page de liste de produits X. Lors de la prochaine ouverture du lien, le produit en question risque de ne plus figurer à la page X.

64
Fionn

Je suis d'accord avec Fionn, mais je vais aller un peu plus loin et me dire que la page n'est pas une ressource, c'est une propriété de la demande. Cela me fait choisir l'option 1 chaîne de requête uniquement. Cela me semble juste. J'aime beaucoup la façon dont la API Twitter est structurée de manière reposante. Pas trop simple, pas trop compliqué, bien documenté. Pour le meilleur ou pour le pire, c’est mon style de prédilection lorsque je suis sur le point de faire quelque chose d’une manière ou d’une autre.

104
slf

HTTP a un excellent en-tête Range qui convient également à la pagination. Vous pouvez envoyer

Range: pages=1

avoir seulement la première page. Cela peut vous obliger à repenser ce qu'est une page. Peut-être que le client veut une gamme différente d'articles. En-tête de plage fonctionne également pour déclarer une commande:

Range: products-by-date=2009_03_27-

pour obtenir tous les produits plus récents que cette date ou

Range: products-by-date=0-2009_11_30

pour obtenir tous les produits plus anciens que cette date. '0' n'est probablement pas la meilleure solution, mais RFC semble vouloir quelque chose pour le début de la plage. Il se peut que des analyseurs HTTP déployés n'analysent pas units = -range_end.

Si les en-têtes ne constituent pas une option (acceptable), j'estime que la première solution (toutes les chaînes de requête) est un moyen de traiter les pages. Mais s'il vous plaît, normalisez les chaînes de requête (les paires de tri (clé = valeur) dans l'ordre alphabétique). Cela résout le problème de différenciation "? A = 1 & b = x" et "? B = x & a = 1".

35
temoto

L'option 1 semble la meilleure, dans la mesure où votre application considère la pagination comme une technique permettant de produire une vue différente de la même ressource.

Cela dit, le schéma d'URL est relativement insignifiant. Si vous concevez votre application comme étant piloté par l'hypertexte (comme tous REST doit être par définition), votre client ne construira pas d’URI lui-même, mais votre application donnera les liens au client et le client les suivra.

Un type de lien que votre client peut fournir est un lien de pagination.

L'effet secondaire agréable de tout cela est que même si vous changez d'avis sur la structure d'URI de pagination et que vous implémentez quelque chose de totalement différent la semaine prochaine, vos clients peuvent continuer à travailler sans aucune modification.

24
Rich Apodaca

J'ai toujours utilisé le style de l'option 1. La mise en cache n'a pas été une préoccupation car les données changent fréquemment de toute façon dans mon cas. Si vous autorisez la configuration de la taille de la page, les données ne peuvent plus être mises en cache.

Je ne trouve pas l'url difficile à retenir ou malpropre. Pour moi, c'est une bonne utilisation des paramètres de requête. La ressource est clairement une liste de produits et les paramètres de requête indiquent simplement comment vous souhaitez afficher la liste - triée et quelle page.

11
John Snyders

Etrange que personne n'ait signalé que l'option 3 dispose de paramètres dans un ordre spécifique. http // application/produits/Date/décroissant/nom/croissant/page/2 et http // application/produits/Nom/croissant/date/décroissant/page/2

pointent vers la même ressource, mais ont des URL complètement différentes.

Pour moi, l'option 1 semble la plus acceptable, puisqu'elle sépare clairement "Ce que je veux" et "Comme je le veux" le (Il a même un point d'interrogation entre eux lol). La mise en cache pleine page peut être implémentée en utilisant une URL complète (toutes les options souffriront du même problème).

Avec l'approche Parameters-in-URL, le seul avantage est une URL propre. Bien que vous deviez trouver un moyen de coder des paramètres et de les décoder sans perte. Bien sûr, vous pouvez utiliser URLencode/decode, mais cela rendra à nouveau les urls moche :)

8
TEHEK

Je préférerais utiliser les paramètres de requête offset et limit.

offset : pour l'index de l'élément dans la collection.

limit : pour le nombre d'éléments.

Le client peut simplement continuer à mettre à jour le décalage comme suit

offset = offset + limit

pour la page suivante.

Le chemin est considéré comme l'identifiant de la ressource. Et une page n'est pas une ressource mais un sous-ensemble de la collection de ressources. La pagination étant généralement une requête GET, les paramètres de requête conviennent mieux à la pagination qu'aux en-têtes.

J'utilise metamug . Ils ont cette configurable. pagination sur métamug de requête sélectionnée

6
Sorter

J'utilise actuellement un schéma similaire à celui-ci dans mes applications ASP.NET MVC:

par exemple. http://application/products/by-date/page/2

en particulier c'est: http://application/products/Date/Ascending/3

Cependant, je ne suis pas vraiment content d'inclure de cette façon des informations de pagination et de tri dans l'itinéraire.

La liste des articles (produits dans ce cas) est modifiable. C'est-à-dire que la prochaine fois que quelqu'un retournera à une URL comprenant des paramètres de pagination et de tri, les résultats obtenus pourraient avoir changé. Donc, l'idée de http://application/products/Date/Ascending/3 comme une URL unique qui pointe vers un ensemble de produits défini et immuable est perdue.

4
Steve Willcock

À la recherche de meilleures pratiques, je suis tombé sur ce site:

http://www.restapitutorial.com

Dans la page des ressources, vous trouverez un lien pour télécharger un fichier .pdf contenant les meilleures pratiques complètes REST suggérées par l'auteur), dans lesquelles se trouve notamment une section sur la pagination.

L'auteur suggère d'ajouter un support pour l'utilisation d'un en-tête Range et de paramètres de chaîne de requête.

Requête

Exemple d'en-tête HTTP:

Range: items=0-24

Exemple de paramètres de chaîne de requête:

GET http://api.example.com/resources?offset=0&limit=25

offset est le numéro de début de l'élément et limite est le nombre maximal d'éléments à renvoyer.

Réponse

La réponse doit inclure un en-tête Content-Range indiquant le nombre d'éléments renvoyés et le nombre total d'éléments restant à extraire.

Exemples d'en-tête HTTP:

Content-Range: items 0-24/66

Content-Range: items 40-65/*

Le fichier .pdf contient d'autres suggestions pour des cas plus spécifiques.

3
Mario Arturo

J'ai tendance à être d'accord avec slf pour dire que "page" n'est pas vraiment une ressource. D'autre part, l'option 3 est plus propre, plus facile à lire et peut être plus facilement devinée par l'utilisateur et même tapée si nécessaire. Je suis déchiré entre les options 1 et 3, mais je ne vois aucune raison de ne pas utiliser l'option 3.

De plus, bien qu'ils aient l'air sympa, l'un des inconvénients de l'utilisation de paramètres cachés, comme quelqu'un l'a mentionné, plutôt que de chaînes de requête ou de segments d'URL, est que l'utilisateur ne peut pas marquer de signets ou créer un lien direct vers une page particulière. Cela peut être ou ne pas être un problème en fonction de l'application, mais juste quelque chose à être au courant.

1
insane.dreamer

J'ai déjà utilisé la solution 3 auparavant (j'écris BEAUCOUP d 'Django). Et je ne pense pas qu'il y ait quelque chose qui cloche dans cette solution. Elle est aussi générable que les deux autres (en cas En outre, vos utilisateurs peuvent deviner les URL (s’il s’agit d’une application grand public), et les gens aiment pouvoir aller directement là où ils le souhaitent, et deviner des URL leur donne du pouvoir. .

0
Alex

J'utilise dans mes projets les URL suivantes:

http://application/products?page=2&sort=+field1-field2

ce qui signifie - "donnez-moi page la deuxième page ordonnée en ordre croissant par field1 puis en descendant par field2". Ou si j'ai besoin de plus de flexibilité, j'utilise:

http://application/products?skip=20&limit=20&sort=+field1-field2
0
Eugene