133 votes

Pagination dans une Collection de repos

Je suis intéressé dans le fait d'exposer directement le REPOS de l'interface pour les collections de documents JSON (pensez à CouchDB ou Persévérer). Le problème, je suis en cours d'exécution dans est comment gérer l' GET opération sur la racine collection si la collection est importante.

Comme exemple de faire semblant, je suis d'exposer StackOverflow de l' Questions tableau où chaque ligne est exposée dans un document (pas qu'il y ait nécessairement est un tableau, un exemple concret d'une importante collection de "documents"). La collection sera disponible à l' /db/questions avec l'habitude CRUD api GET /db/questions/XXX, PUT /db/questions/XXX, POST /db/questions est en jeu. La méthode standard pour obtenir l'intégralité de la collection est-à - GET /db/questions mais si naïvement décharges chaque ligne comme un objet JSON, vous obtiendrez un groupe assez nombreux de téléchargement et beaucoup de travail de la part du serveur.

La solution est, bien sûr, d'échange. Dojo a résolu ce problème dans son JsonRestStore via un astucieux RFC2616-conforme à l'extension de l'aide de l' Range - tête avec un éventail personnalisé de l'unité items. Le résultat est un 206 Partial Content qui retourne uniquement la plage requise. L'avantage de cette approche sur un paramètre de requête, c'est qu'il quitte la chaîne de requête pour...les requêtes (par exemple, GET /db/questions/?score>200 ou somesuch, et oui ce serait codé %3E).

Cette approche couvre complètement le comportement que je veux. Le problème est que la RFC 2616 précise que sur une 206 de réponse (l'emphase est mienne):

La demande DOIT inclure une Gamme de champ d'en-tête (section 14.35) indiquant la plage souhaitée, et PEUT-être une Si-Gamme champ d'en-tête (section 14.27) pour en faire la demande conditionnelle.

Cela a un sens dans le contexte de l'utilisation standard de l'en-tête mais c'est un problème parce que j'aimerais que la 206 de réponse par défaut pour gérer les naïfs clients/personnes au hasard de l'exploration.

J'ai dépassé les RFC en détail à la recherche d'une solution, mais ont été malheureux avec mes solutions, et j'aimerais DONC prendre le problème.

Les idées que j'ai eu:

  • De retour 200 avec un Content-Range - tête! - Je ne pense pas que c'est mal, mais je préfère si un plus évident, voyant que la réponse n'est que Partielle du Contenu.
  • De retour 400 Range Required - Il n'y a pas un spécial 400 code de réponse pour les en-têtes, de sorte que l'erreur par défaut doit être utilisé et lire par la main. Cela rend également l'exploration via un navigateur web (ou un autre client comme Resty) plus difficile.
  • L'utilisation d'un paramètre de requête - L'approche standard, mais je suis l'espoir d'autoriser les requêtes à la Persévérer et présente des coupes dans la requête de l'espace de noms.
  • Juste retour 206! - Je pense que la plupart des clients de ne pas paniquer, mais je préfère ne pas aller à l'encontre d'un must dans la RFC
  • Étendre la spec! De retour 266 Partial Content - se Comporte exactement comme la 206 mais est en réponse à une demande qui ne DOIT PAS contenir de l' Range - tête. Je figure que 266 est suffisamment élevé pour que je ne devrais pas courir dans la collision de problèmes et qu'il fait sens pour moi, mais je ne suis pas clair si cela est considéré comme un tabou ou pas.

J'avais pense que c'est un problème assez courant et j'aimerais voir ce fait dans une sorte de facto de la mode donc je ou quelqu'un d'autre n'est pas de réinventer la roue.

Quelle est la meilleure façon d'exposer une collection complète via HTTP, lorsque la collection est importante?

34voto

Mohamed Points 377

Je n'ai pas vraiment d'accord avec certains d'entre vous les gars. J'ai travaillé pendant des semaines sur cette fonctionnalité pour mon service REST. Ce que j'ai fini par faire, c'est vraiment simple. Ma solution ne fait qu'un sens de ce qui RESTE de gens appellent une collection.

Le Client DOIT comprendre une "Gamme" de l'en-tête pour indiquer quelle partie de la collection dont il a besoin, ou autrement être prêt à gérer un 416 PLAGE DEMANDÉE n'est PAS correcte d'erreur lors de l'enlèvement demandé est trop grand pour être récupérées dans un seul aller-retour.

Le serveur envoie une 206 PARTIELLE, du CONTENU de la réponse, avec le Contenu de Gamme en-tête spécifiant une partie de la ressource a été envoyé, et d'un en-tête ETag pour identifier la version actuelle de la collection. J'ai l'habitude d'utiliser un Facebook-like ETag {last_modification_timestamp}-{resource_id}, et je considère que l'ETag d'une collection, c'est que de la modification la plus récente de ressources qu'il contient.

À la demande d'une partie spécifique du prélèvement, le client DOIT utiliser le "Range" de l'en-tête, et de remplir le "Si-Match" en-tête avec l'ETag de la collection obtenue à partir d'précédemment effectué des demandes d'acquérir d'autres pièces de la même collection. Le serveur peut donc vérifier que la collection n'a pas changé avant l'envoi de la demande de la partie. Si une version plus récente existe, une CONDITION préalable 412 ÉCHEC réponse est renvoyée à inviter le client à récupérer la collection à partir de zéro. Cela est nécessaire parce que cela pourrait signifier que certaines ressources peuvent avoir été ajoutés ou supprimés avant ou après la partie demandée.

J'utilise ETag/Si-Match en tandem avec Last-Modified/If-Unmodified-Since pour optimiser le cache. Les navigateurs et les procurations peuvent compter sur l'un ou les deux d'entre eux pour leurs algorithmes de mise en cache.

Je pense qu'une URL doit être propre, sauf à inclure une recherche/filtre de requête. Si vous pensez à ce sujet, une recherche est rien de plus qu'une vue partielle d'une collection. Au lieu de les voitures/search?q=BMW type d'Url, nous devrions voir plus de voitures?fabricant=BMW.

23voto

Julian Reschke Points 12698

Mon sentiment profond est que le HTTP extensions de gammes ne sont pas conçus pour votre cas d'utilisation, et donc vous ne devriez pas essayer. Une réponse partielle implique 206, et 206 doivent être envoyés uniquement si le client le demande.

Vous souhaitez mai à envisager une approche différente, comme celui utilisé dans l'Atome (où la représentation par le dessin peut être partielle, et est retourné avec un statut d' 200, et potentiellement liens de pagination). Voir la RFC 4287 et RFC 5005.

5voto

Vitorio Points 506

Si il n'y a plus d'une page de réponses, et vous ne voulez pas offrir à l'ensemble de la collection, à la fois, est-ce à dire il y a plusieurs choix?

Sur une demande d' /db/questions, rendement 300 Multiple Choices avec Link - têtes, qui précisent comment accéder à chaque page ainsi qu'un objet JSON ou une page HTML avec une liste d'Url.

Link: <>; rel="http://paged.collection.example/relation/paged"
Link: <>; rel="http://paged.collection.example/relation/paged"
...

Vous auriez un Link - tête de chaque page de résultats (une chaîne vide signifie que l'URL et l'URL est la même pour chaque page, seulement accessible avec différentes gammes), et la relation est défini comme un programme personnalisé pour les prochaines Link spec. Cette relation pourrait expliquer votre coutume 266, ou de votre violation de l' 206. Ces en-têtes sont vos lisibles à la machine version, puisque tous vos exemples nécessitent une compréhension du client, de toute façon.

(Si vous vous en tenez à la "gamme" de la route, je crois que votre propre 2xx code de retour, comme vous l'avez décrit, serait le meilleur comportement ici. Vous êtes attendus pour ce faire à vos applications et à de telles ["HTTP status codes sont extensibles."], et vous avez de bonnes raisons).

300 Multiple Choices dit vous DEVEZ également fournir un corps avec un moyen pour l'utilisateur de l'agent à choisir. Si votre client est de comprendre, il doit utiliser l' Link - têtes. Si c'est un utilisateur manuellement la navigation, peut-être une page HTML avec des liens vers un spécial "paginé" racine de ressources qui peuvent gérer le rendu de cette page en fonction de l'URL? /humanpage/1/db/questions ou quelque chose de hideux comme ça?


Les commentaires sur Richard Levasseur post me rappelle une option supplémentaire: l' Accept - tête (article 14.1). Quand le spec oembed, il est sorti, je me suis demandé pourquoi il n'avait pas été fait entièrement à l'aide de HTTP, et a écrit une alternative à l'aide.

Garder l' 300 Multiple Choices, Link - têtes et la page HTML pour un premier naïf HTTP GET, mais plutôt que d'utiliser des plages, votre nouveau pagination relation de définir l'utilisation de l' Accept - tête. Votre requête HTTP peut ressembler à ceci:

GET /db/questions HTTP/1.1
Host: paged.collection.example
Accept: application/json;PagingSpec=1.0;page=1

L' Accept d'en-tête vous permet de définir un niveau acceptable de type de contenu (votre retour JSON), plus extensible paramètres de ce type (votre numéro de page). Riffs sur mes notes de mon oembed, il writeup (peut pas le lien ici, je vais l'inscrire dans mon profil), vous pourriez être très explicite et de fournir un spec/rapport version ici dans le cas où vous avez besoin de redéfinir ce que l' page paramètre signifie que dans l'avenir.

4voto

Richard Levasseur Points 5428

Edit:

Après y avoir réfléchi un peu plus, je suis enclin à penser que la Gamme en-têtes ne sont pas appropriés pour la pagination. La logique étant, la Gamme d'en-tête est prévu pour la réponse du serveur, pas les applications. Si vous sert plus de 100 mégaoctets de résultats, mais le serveur (ou le client) ne pouvaient traiter 1 mégaoctet à un moment, eh bien, c'est ce que la Gamme d'en-tête de est pour.

Je suis également d'avis qu'un sous-ensemble de ressources est de ses propres ressources (semblable à de l'algèbre relationnelle.), donc, il mérite la représentation dans l'URL.

Donc, fondamentalement, je abjurer ma réponse originale à cette question (ci-dessous) sur l'utilisation d'un en-tête.


Je pense que vous avez répondu à votre propre question, plus ou moins - retour de 200 ou 206 avec le contenu de gamme et éventuellement utiliser un paramètre de requête. Je voudrais renifler l'agent de l'utilisateur et du type de contenu, et en fonction de ceux, vérifier un paramètre de requête. Autrement, exigent la gamme des en-têtes.

Vous avez des objectifs contradictoires - de laisser les gens utiliser leur navigateur explorer (qui n'est pas facilement permettre d'en-têtes personnalisés), ou de forcer les gens à utiliser un client spécial que l'on peut définir des en-têtes (ce qui ne veut pas les laisser explorer).

Vous pourriez seulement fournir avec le client en fonction de la demande - si ça ressemble à un simple navigateur, envoyer une petite application ajax qui rend la page et définit l'entête nécessaire.

Bien sûr, il y a aussi le débat quant à savoir si l'URL doit contenir tout le nécessaire de l'état pour ce genre de chose. La spécification de la plage à l'aide des en-têtes peuvent être considérés comme des "nations unies-sommeil" par certains.

En aparté, il serait bien si les serveurs pourraient répondre avec un "Peut-Spécifier: Header1, header2" en-tête et les navigateurs web serait de présenter une INTERFACE utilisateur de sorte que les utilisateurs peuvent remplir leurs valeurs, s'ils le souhaitent.

3voto

dajobe Points 2963

Vous pourriez envisager d'utiliser un modèle de quelque chose comme le Flux Atom du Protocole, puisqu'il a un sane HTTP modèle de collections et comment les manipuler (où les fous de moyens WebDAV).

Il y a le Protocole de Publication Atom, qui définit le modèle de la collection et de REPOS des opérations de plus, vous pouvez utiliser la RFC 5005 - Alimentation de Pagination et de l'Archivage à la page grâce à de grandes collections.

Commutation de l'Atome XML en JSON contenu ne devrait pas affecter l'idée.

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