it-swarm.com.de

Paginierung in einer REST Webanwendung

Dies ist eine allgemeinere Neuformulierung von diese Frage (mit der Beseitigung der Rails spezifischen Teile)

Ich bin nicht sicher, wie die Paginierung einer Ressource in einer RESTful-Webanwendung implementiert werden soll. Angenommen, ich habe eine Ressource namens products. Welche der folgenden Methoden ist Ihrer Meinung nach die beste und warum?

1. Verwenden Sie nur Abfragezeichenfolgen

z.B. http://application/products?page=2&sort_by=date&sort_how=asc
Das Problem hierbei ist, dass ich das Caching für ganze Seiten nicht verwenden kann und auch die URL nicht sehr sauber und leicht zu merken ist.

2. Verwenden von Seiten als Ressourcen und Abfragezeichenfolgen zum Sortieren

z.B. http://application/products/page/2?sort_by=date&sort_how=asc
In diesem Fall besteht das Problem darin, dass http://application/products/pages/1 ist keine eindeutige Ressource, da sort_by=price kann zu einem völlig anderen Ergebnis führen und Ich kann das Seiten-Caching immer noch nicht verwenden.

3. Verwenden von Seiten als Ressourcen und eines URL-Segments zum Sortieren

z.B. http://application/products/by-date/page/2
Ich persönlich sehe kein Problem bei der Verwendung dieser Methode, aber jemand hat mich gewarnt, dass dies kein guter Weg ist (er hat keinen Grund angegeben, also wenn Sie wissen warum es nicht empfohlen wird, lass es mich wissen)

Alle Vorschläge, Meinungen, Kritiken sind mehr als willkommen. Vielen Dank.

323
andi

Ich denke, das Problem mit Version 3 ist eher ein "Sicht" -Problem - sehen Sie die Seite als die Ressource oder die Produkte auf der Seite.

Wenn Sie die Seite als Ressource betrachten, ist dies eine perfekte Lösung, da die Abfrage für Seite 2 immer Seite 2 ergibt.

Wenn Sie jedoch die Produkte auf der Seite als Ressource sehen, besteht das Problem, dass sich die Produkte auf Seite 2 ändern (alte Produkte gelöscht oder was auch immer). In diesem Fall gibt der URI nicht immer die gleichen Ressourcen zurück.

Z.B. Ein Kunde speichert einen Link zur Produktlistenseite X. Wenn der Link das nächste Mal geöffnet wird, befindet sich das betreffende Produkt möglicherweise nicht mehr auf Seite X.

64
Fionn

Ich stimme Fionn zu, aber ich gehe noch einen Schritt weiter und sage, dass die Seite für mich keine Ressource ist, sondern eine Eigenschaft der Anfrage. Daher habe ich nur Abfragezeichenfolge für Option 1 ausgewählt. Es fühlt sich einfach richtig an. Mir gefällt sehr gut, wie die Twitter API strukturiert ist. Nicht zu einfach, nicht zu kompliziert, gut dokumentiert. Ob gut oder schlecht, es ist mein "go to" -Design, wenn ich auf dem Zaun bin und etwas in die eine oder andere Richtung tue.

104
slf

HTTP hat einen großartigen Range-Header, der auch zur Paginierung geeignet ist. Sie können senden

Range: pages=1

nur die erste Seite haben. Das kann Sie dazu zwingen, eine Seite neu zu überdenken. Vielleicht möchte der Kunde ein anderes Sortiment. Der Bereichskopf kann auch zum Deklarieren einer Bestellung verwendet werden:

Range: products-by-date=2009_03_27-

um alle Produkte neuer als dieses Datum zu bekommen oder

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

um alle Produkte älter als dieses Datum zu bekommen. '0' ist wahrscheinlich nicht die beste Lösung, aber RFC scheint etwas für den Bereichsbeginn zu wollen. Möglicherweise sind HTTP-Parser implementiert, die units = -range_end nicht analysieren würden.

Wenn Header keine (akzeptable) Option sind, ist die erste Lösung (alles in einer Abfragezeichenfolge) eine Möglichkeit, mit Seiten umzugehen. Aber bitte normalisieren Sie die Abfragezeichenfolgen (sortieren Sie (Schlüssel = Wert) Paare in alphabetischer Reihenfolge). Dies löst das Differenzierungsproblem "? A = 1 & b = x" und "? B = x & a = 1".

35
temoto

Option 1 scheint die beste Option zu sein, sofern in Ihrer Anwendung die Paginierung als eine Methode zum Erstellen einer anderen Ansicht derselben Ressource betrachtet wird.

Allerdings ist das URL-Schema relativ unbedeutend. Wenn Sie Ihre Anwendung als hypertextgesteuert (wie alle REST) - Anwendungen entwerfen muss per definitionem sein), dann erstellt Ihr Client keine eigenen URIs, sondern Ihre Anwendung gibt die Links an den Client weiter und der Client folgt diesen.

Eine Art von Link, die Ihr Kunde bereitstellen kann, ist ein Paginierungslink.

Das alles hat den angenehmen Nebeneffekt, dass Ihre Kunden auch dann weiterarbeiten können, wenn Sie Ihre Meinung über die Paginierungs-URI-Struktur ändern und nächste Woche etwas völlig anderes implementieren.

24
Rich Apodaca

Ich habe immer den Stil von Option 1 verwendet. Zwischenspeichern war kein Problem, da sich die Daten in meinem Fall sowieso häufig ändern. Wenn Sie zulassen, dass die Größe der Seite konfigurierbar ist, können die Daten nicht zwischengespeichert werden.

Ich finde die URL nicht schwer zu merken oder unrein. Für mich ist dies eine gute Verwendung von Abfrageparametern. Die Ressource ist eindeutig eine Liste von Produkten, und die Abfrageparameter geben lediglich an, wie die Liste angezeigt werden soll - sortiert und auf welcher Seite.

11
John Snyders

Seltsamerweise hat niemand darauf hingewiesen, dass Option 3 Parameter in einer bestimmten Reihenfolge enthält. http // application/products/Date/Descending/Name/Ascending/page/2 und http // application/products/Name/Aufsteigend/Datum/Absteigend/page/2

verweisen auf dieselbe Ressource, haben jedoch völlig unterschiedliche URLs.

Für mich scheint Option 1 die akzeptabelste zu sein, da sie "Was ich will" und "Wie ich will" deutlich voneinander trennt (es gibt sogar ein Fragezeichen zwischen ihnen, lol). Das Zwischenspeichern ganzer Seiten kann unter Verwendung der vollständigen URL implementiert werden (alle Optionen haben ohnehin das gleiche Problem).

Beim Parameter-in-URL-Ansatz besteht der einzige Vorteil in einer sauberen URL. Sie müssen sich jedoch eine Möglichkeit einfallen lassen, Parameter zu kodieren und verlustfrei zu dekodieren. Natürlich kannst du mit URLencode/decode gehen, aber es wird die URLs wieder hässlich machen :)

8
TEHEK

Ich würde es vorziehen, die Abfrageparameter offset und limit zu verwenden.

Offset : für den Index des Elements in der Sammlung.

limit : für die Anzahl der Artikel.

Der Client kann den Offset einfach wie folgt aktualisieren

offset = offset + limit

für die nächste Seite.

Der Pfad wird als Ressourcen-ID betrachtet. Und eine Seite ist keine Ressource, sondern eine Teilmenge der Ressourcensammlung. Da Paginierung im Allgemeinen eine GET-Anforderung ist, sind Abfrageparameter eher für Paginierung als für Header geeignet.

Ich benutze Metamug . Sie haben dies konfigurierbar. Paginierung bei Auswahl des Abfrage-Metamugs

6
Sorter

Ich verwende derzeit in meinen ASP.NET MVC-Apps ein ähnliches Schema:

z.B. http://application/products/by-date/page/2

speziell ist es: http://application/products/Date/Ascending/3

Ich bin jedoch nicht sehr glücklich darüber, auf diese Weise Paging- und Sortierinformationen in die Route aufzunehmen.

Die Liste der Artikel (in diesem Fall der Produkte) kann geändert werden. das nächste Mal, wenn jemand zu einer URL zurückkehrt, die Paging- und Sortierparameter enthält, können sich die Ergebnisse geändert haben. Also die Idee von http://application/products/Date/Ascending/3 als eindeutige URL, die auf einen definierten, unveränderlichen Satz von Produkten verweist, geht verloren.

4
Steve Willcock

Auf der Suche nach Best Practices bin ich auf diese Website gestoßen:

http://www.restapitutorial.com

Auf der Ressourcenseite befindet sich ein Link zum Herunterladen einer PDF-Datei, die die vollständigen vom Autor empfohlenen REST) Best Practices enthält. In diesem Abschnitt finden Sie unter anderem Informationen zur Paginierung.

Der Autor schlägt vor, Unterstützung sowohl für die Verwendung eines Range-Headers als auch für die Verwendung von Abfragezeichenfolgenparametern hinzuzufügen.

Anfrage

Beispiel für einen HTTP-Header:

Range: items=0-24

Beispiel für Abfragezeichenfolgenparameter:

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

Wobei Versatz die anfängliche Artikelnummer und Limit die maximale Anzahl der zurückzugebenden Artikel ist.

Antwort

Die Antwort sollte einen Inhaltsbereich-Header enthalten, der angibt, wie viele Elemente zurückgegeben werden und wie viele Elemente insgesamt noch vorhanden sind, um abgerufen zu werden

Beispiele für HTTP-Header:

Content-Range: items 0-24/66

Content-Range: items 40-65/*

In der .pdf gibt es einige andere Vorschläge für spezifischere Fälle.

3
Mario Arturo

Ich stimme SLF eher zu, dass "Seite" nicht wirklich eine Ressource ist. Auf der anderen Seite ist Option 3 übersichtlicher, leichter zu lesen und kann vom Benutzer leichter erraten und bei Bedarf sogar getippt werden. Ich bin zwischen Option 1 und 3 hin und her gerissen, sehe aber keinen Grund, Option 3 nicht zu verwenden.

Auch wenn sie gut aussehen, besteht ein Nachteil der Verwendung versteckter Parameter, wie bereits erwähnt, anstelle von Abfragezeichenfolgen oder URL-Segmenten darin, dass der Benutzer keine Lesezeichen setzen oder keine direkten Links zu einer bestimmten Seite erstellen kann. Das kann je nach Anwendung ein Problem sein oder auch nicht, aber es muss nur beachtet werden.

1
insane.dreamer

Ich habe zuvor Lösung 3 verwendet (ich schreibe VIELE Django apps). Und ich glaube nicht, dass irgendetwas daran falsch ist. Es ist genauso generierbar wie die anderen beiden (incase) Sie müssen ein bisschen massenschaben oder ähnliches tun) und es sieht sauberer aus. Außerdem können Ihre Benutzer URLs erraten (wenn es sich um eine öffentlich zugängliche App handelt), und die Leute mögen es, direkt dorthin zu gelangen, wo sie wollen, und das Erraten von URLs fühlt sich befähigend an .

0
Alex

Ich verwende in meinen Projekten die folgenden URLs:

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

was bedeutet - "gib mir Seite die zweite Seite in aufsteigender Reihenfolge von Feld1 und dann absteigender Reihenfolge von Feld2". Oder wenn ich noch mehr Flexibilität brauche, benutze ich:

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