Ich entwickle einen REST-API-Service für eine große Website für soziale Netzwerke, an der ich beteiligt bin. Bisher funktioniert er hervorragend. Ich kann ausgeben GET
, POST
, PUT
und DELETE
Anfragen an Objekt - URLs und beeinflussen meine Daten. Diese Daten werden jedoch ausgelagert (auf jeweils 30 Ergebnisse begrenzt).
Was wäre jedoch der beste REST-Weg, um die Gesamtzahl der Mitglieder über meine API zu ermitteln?
Derzeit stelle ich Anforderungen an eine URL-Struktur wie die folgende:
- / api / members - Gibt eine Liste der Mitglieder zurück (jeweils 30 wie oben erwähnt).
- / api / member / 1 - Beeinflusst ein einzelnes Mitglied, abhängig von der verwendeten Anforderungsmethode
Meine Frage ist: Wie würde ich dann eine ähnliche URL-Struktur verwenden, um die Gesamtzahl der Mitglieder in meiner Bewerbung zu erhalten? Offensichtlich id
wäre es unwirksam , nur das Feld anzufordern (ähnlich wie bei der Graph-API von Facebook) und die Ergebnisse zu zählen, da nur ein Teil von 30 Ergebnissen zurückgegeben würde.
quelle
Antworten:
Während die Antwort an / API / users ausgelagert ist und nur 30 Datensätze zurückgibt, hindert Sie nichts daran, auch die Gesamtzahl der Datensätze und andere relevante Informationen wie Seitengröße, Seitenzahl / Versatz usw. In die Antwort aufzunehmen .
Die StackOverflow-API ist ein gutes Beispiel für dasselbe Design. Hier ist die Dokumentation für die Users-Methode - https://api.stackexchange.com/docs/users
quelle
Ich bevorzuge die Verwendung von HTTP-Headern für diese Art von Kontextinformationen.
Für die Gesamtzahl der Elemente verwende ich den
X-total-count
Header.Für Links zur nächsten, vorherigen Seite usw. verwende ich den http-
Link
Header:http://www.w3.org/wiki/LinkHeader
Github macht es genauso: https://developer.github.com/v3/#pagination
Meiner Meinung nach ist es sauberer, da es auch verwendet werden kann, wenn Sie Inhalte zurückgeben, die keine Hyperlinks unterstützen (dh Binärdateien, Bilder).
quelle
X-
.Ich habe in letzter Zeit einige umfangreiche Untersuchungen zu dieser und anderen Fragen im Zusammenhang mit REST-Paging durchgeführt und fand es konstruktiv, einige meiner Ergebnisse hier hinzuzufügen. Ich erweitere die Frage ein wenig, um Gedanken zum Paging sowie zur Zählung einzubeziehen, da sie eng miteinander verbunden sind.
Überschriften
Die Paging-Metadaten sind in Form von Antwortheadern in der Antwort enthalten. Der große Vorteil dieses Ansatzes besteht darin, dass die Antwortnutzlast selbst nur die tatsächliche Datenanforderung ist, nach der gefragt wurde. Erleichterung der Verarbeitung der Antwort für Clients, die nicht an den Paging-Informationen interessiert sind.
Es gibt eine Reihe von (Standard- und benutzerdefinierten) Headern, die in freier Wildbahn verwendet werden, um Paging-bezogene Informationen zurückzugeben, einschließlich der Gesamtzahl.
X-Total-Count
Dies wird in einigen APIs verwendet, die ich in freier Wildbahn gefunden habe. Es gibt auch NPM-Pakete zum Hinzufügen von Unterstützung für diesen Header zu z. B. Loopback. In einigen Artikeln wird empfohlen, auch diesen Header festzulegen.
Es wird häufig in Kombination mit dem
Link
Header verwendet. Dies ist eine ziemlich gute Lösung für das Paging, es fehlen jedoch die Informationen zur Gesamtanzahl.Verknüpfung
Ich bin der Meinung, dass nach allgemeiner Lektüre zu diesem Thema der allgemeine Konsens darin besteht, den
Link
Header zu verwenden, um Paging-Links für Clients bereitzustellenrel=next
, dierel=previous
usw. verwenden. Das Problem dabei ist, dass es an Informationen darüber mangelt, wie viele Datensätze insgesamt vorhanden sind warum viele APIs dies mit demX-Total-Count
Header kombinieren .Alternativ verwenden einige APIs und z. B. der JsonApi- Standard das
Link
Format, fügen die Informationen jedoch in einem Antwortumschlag anstelle eines Headers hinzu. Dies vereinfacht den Zugriff auf die Metadaten (und schafft einen Ort zum Hinzufügen der Gesamtanzahlinformationen) auf Kosten einer zunehmenden Komplexität des Zugriffs auf die tatsächlichen Daten selbst (durch Hinzufügen eines Umschlags).Inhaltsbereich
Gefördert durch einen Blog-Artikel namens Range Header, wähle ich Sie (für die Paginierung)! . Der Autor spricht sich stark dafür aus, die Überschriften
Range
undContent-Range
für die Paginierung zu verwenden. Wenn wir den RFC in diesen Headern sorgfältig lesen , stellen wir fest, dass die Erweiterung ihrer Bedeutung über Bytebereiche hinaus vom RFC tatsächlich vorweggenommen wurde und ausdrücklich zulässig ist. Wenn der Bereichskopf im Kontext vonitems
anstelle von verwendet wirdbytes
, können wir sowohl einen bestimmten Bereich von Elementen anfordern als auch angeben, auf welchen Bereich des Gesamtergebnisses sich die Antwortelemente beziehen. Dieser Header bietet auch eine großartige Möglichkeit, die Gesamtzahl anzuzeigen. Und es ist ein echter Standard, der Paging meistens eins zu eins zuordnet. Es wird auch in freier Wildbahn verwendet .Briefumschlag
Viele APIs, einschließlich der von unserer bevorzugten Q & A-Website, verwenden einen Umschlag , einen Wrapper um die Daten, mit dem Metainformationen zu den Daten hinzugefügt werden. Auch OData und JsonApi Standards einen .
Der große Nachteil (imho) ist, dass die Verarbeitung der Antwortdaten komplexer wird, da die tatsächlichen Daten irgendwo im Umschlag gefunden werden müssen. Es gibt auch viele verschiedene Formate für diesen Umschlag und Sie müssen das richtige verwenden. Es ist bezeichnend, dass die Antwortumschläge von OData und JsonApi sehr unterschiedlich sind, wobei OData an mehreren Stellen in der Antwort Metadaten einmischt.
Separater Endpunkt
Ich denke, dies wurde in den anderen Antworten ausreichend behandelt. Ich habe nicht so viel untersucht, weil ich den Kommentaren zustimme, dass dies verwirrend ist, da Sie jetzt mehrere Arten von Endpunkten haben. Ich finde es am schönsten, wenn jeder Endpunkt eine (Sammlung von) Ressourcen darstellt.
Weitere Gedanken
Wir müssen nicht nur die Paging-Metainformationen in Bezug auf die Antwort kommunizieren, sondern dem Client auch erlauben, bestimmte Seiten / Bereiche anzufordern. Es ist interessant, auch diesen Aspekt zu betrachten, um eine kohärente Lösung zu erhalten. Auch hier können wir Header (der
Range
Header scheint sehr geeignet zu sein) oder andere Mechanismen wie Abfrageparameter verwenden. Einige Leute befürworten die Behandlung von Seiten der Ergebnisse als separate Ressourcen, der Sinn in einigen Anwendungsfällen machen kann (zB/books/231/pages/52
. Ich landete eine wilde Reihe von häufig verwendeten Anforderungsparametern Auswahl wiepagesize
,page[size]
undlimit
usw. zusätzlich zur Unterstützung derRange
Header (und als Anforderungsparameter auch).quelle
Range
Headern interessiert , konnte jedoch nicht genügend Beweise dafür finden, dass die Verwendung von etwas anderembytes
als einem Bereichstyp gültig ist.acceptable-ranges = 1#range-unit | "none"
Ich denke, diese Formulierung lässt explizit Raum für andere Bereichseinheiten alsbytes
, obwohl die Spezifikation selbst nur definiertbytes
.Alternative, wenn Sie keine tatsächlichen Artikel benötigen
Die Antwort von Franci Penov ist sicherlich der beste Weg, sodass Sie immer Artikel zusammen mit allen zusätzlichen Metadaten zu Ihren angeforderten Entitäten zurückgeben. So sollte es gemacht werden.
Manchmal ist es jedoch nicht sinnvoll, alle Daten zurückzugeben, da Sie sie möglicherweise überhaupt nicht benötigen. Vielleicht brauchen Sie nur diese Metadaten zu Ihrer angeforderten Ressource. Wie Gesamtzahl oder Anzahl der Seiten oder etwas anderes. In diesem Fall kann eine URL-Abfrage Ihren Dienst jederzeit anweisen, keine Elemente zurückzugeben, sondern nur Metadaten wie:
oder etwas ähnliches...
quelle
metaonly
oderincludeitems
nicht.Sie können die Anzahl als benutzerdefinierten HTTP-Header als Antwort auf eine HEAD-Anforderung zurückgeben. Auf diese Weise müssen Sie die tatsächliche Liste nicht zurückgeben, wenn ein Client nur die Zählung wünscht, und es ist keine zusätzliche URL erforderlich.
(Wenn Sie sich in einer kontrollierten Umgebung von Endpunkt zu Endpunkt befinden, können Sie ein benutzerdefiniertes HTTP-Verb wie COUNT verwenden.)
quelle
Ich würde empfehlen, Header für das gleiche hinzuzufügen, wie:
Einzelheiten finden Sie unter:
https://github.com/adnan-kamili/rest-api-response-format
Für Swagger-Datei:
https://github.com/adnan-kamili/swagger-response-template
quelle
Ab "X -" - Präfix war veraltet. (siehe: https://tools.ietf.org/html/rfc6648 )
Wir fanden, dass die "Accept-Ranges" die beste Wahl sind, um den Paginierungsbereich abzubilden: https://tools.ietf.org/html/rfc7233#section-2.3 Da die "Range Units" entweder "Bytes" oder " Zeichen". Beide repräsentieren keinen benutzerdefinierten Datentyp. (siehe: https://tools.ietf.org/html/rfc7233#section-4.2 ) Dennoch wird angegeben, dass
Was darauf hinweist: Die Verwendung von benutzerdefinierten Bereichseinheiten verstößt nicht gegen das Protokoll, kann jedoch ignoriert werden.
Auf diese Weise müssten wir die Accept-Ranges auf "Mitglieder" oder einen beliebigen Typ von Fernkampfeinheiten setzen, den wir erwarten würden. Stellen Sie außerdem den Inhaltsbereich auf den aktuellen Bereich ein. (siehe: https://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.12 )
In jedem Fall würde ich mich an die Empfehlung von RFC7233 ( https://tools.ietf.org/html/rfc7233#page-8 ) halten, eine 206 anstelle von 200 zu senden:
Als Ergebnis hätten wir also die folgenden HTTP-Headerfelder:
Für Teilinhalte:
Für den vollständigen Inhalt:
quelle
Scheint am einfachsten, einfach a hinzuzufügen
und geben Sie die Gesamtzahl der Mitglieder zurück
quelle
Was ist mit einem neuen Endpunkt> / api / members / count, der nur Members.Count () aufruft und das Ergebnis zurückgibt?
quelle
members
Sammlung durch eine POST-Anfrage an erstellt werden kann/api
, wird/api/members/count
sie auch als Nebeneffekt erstellt, oder muss ich eine explizite POST-Anfrage erstellen, um sie zu erstellen, bevor ich sie anfordere? :-)Manchmal erfordern Frameworks (wie $ resource / AngularJS) ein Array als Abfrageergebnis, und Sie können nicht wirklich eine Antwort wie erhalten
{count:10,items:[...]}
in diesem Fall speichere ich "count" in responseHeaders.PS Eigentlich kann man das mit $ resource / AngularJS machen, aber es braucht einige Verbesserungen.
quelle
isArray: false|true
Sie könnten
counts
als Ressource betrachten. Die URL wäre dann:quelle
Wenn Sie paginierte Daten anfordern, kennen Sie (anhand des expliziten Seitengrößenparameterwerts oder des Standardseitengrößenwerts) die Seitengröße, sodass Sie wissen, ob Sie alle Daten als Antwort erhalten haben oder nicht. Wenn weniger Daten als eine Seitengröße antworten, erhalten Sie ganze Daten. Wenn eine ganze Seite zurückgegeben wird, müssen Sie erneut nach einer anderen Seite fragen.
Ich bevorzuge einen separaten Endpunkt für count (oder denselben Endpunkt mit dem Parameter countOnly). Weil Sie den Endbenutzer auf einen langen / zeitaufwändigen Prozess vorbereiten können, indem Sie den ordnungsgemäß initiierten Fortschrittsbalken anzeigen.
Wenn Sie in jeder Antwort die Datengröße zurückgeben möchten, sollte die Seitengröße und der Offset ebenfalls angegeben sein. Um ehrlich zu sein, ist der beste Weg, auch einen Anforderungsfilter zu wiederholen. Die Reaktion wurde jedoch sehr komplex. Daher bevorzuge ich einen dedizierten Endpunkt, um die Anzahl zurückzugeben.
Couleage von mir, ziehen Sie einen countOnly-Parameter dem vorhandenen Endpunkt vor. Wenn angegeben, enthält die Antwort nur Metadaten.
Endpunkt? Filter = Wert
Endpunkt? filter = value & countOnly = true
quelle