Best Practice für die REST-API: Akzeptieren der Liste der Parameterwerte als Eingabe [geschlossen]


409

Wir starten eine neue REST-API und ich wollte einige Community-Beiträge zu Best Practices für die Formatierung von Eingabeparametern:

Derzeit ist unsere API sehr JSON-zentriert (gibt nur JSON zurück). Die Debatte darüber, ob wir XML zurückgeben wollen / müssen, ist ein separates Thema.

Da unsere API-Ausgabe JSON-zentriert ist, haben wir einen Weg eingeschlagen, in dem unsere Eingaben etwas JSON-zentriert sind, und ich habe gedacht, dass dies für einige praktisch, aber im Allgemeinen seltsam sein könnte.

Um beispielsweise einige Produktdetails zu erhalten, bei denen mehrere Produkte gleichzeitig abgerufen werden können, haben wir derzeit:

http://our.api.com/Product?id=["101404","7267261"]

Sollten wir dies vereinfachen als:

http://our.api.com/Product?id=101404,7267261

Oder ist eine JSON-Eingabe praktisch? Eher ein Schmerz?

Wir möchten vielleicht beide Stile akzeptieren, aber verursacht diese Flexibilität tatsächlich mehr Verwirrung und Kopfschmerzen (Wartbarkeit, Dokumentation usw.)?

Ein komplexerer Fall ist, wenn wir komplexere Eingaben anbieten möchten. Wenn wir beispielsweise mehrere Filter für die Suche zulassen möchten:

http://our.api.com/Search?term=pumas&filters={"productType":["Clothing","Bags"],"color":["Black","Red"]}

Wir möchten die Filtertypen (z. B. Produkttyp und Farbe) nicht unbedingt als Anforderungsnamen wie folgt einfügen:

http://our.api.com/Search?term=pumas&productType=["Clothing","Bags"]&color=["Black","Red"]

Weil wir alle Filtereingaben zusammenfassen wollten.

Ist das am Ende wirklich wichtig? Es ist wahrscheinlich, dass es so viele JSON-Utils gibt, dass der Eingabetyp einfach nicht so wichtig ist.

Ich weiß, dass unsere JavaScript-Clients, die AJAX-Aufrufe an die API ausführen, die JSON-Eingaben möglicherweise schätzen, um ihnen das Leben zu erleichtern.

Antworten:


341

Ein Schritt zurück

In erster Linie beschreibt REST eine URI als universell eindeutige ID. Viel zu viele Menschen sind mit der Struktur von URIs beschäftigt und welche URIs "erholsamer" sind als andere. Dieses Argument ist so lächerlich wie zu sagen, dass es besser ist, jemanden "Bob" zu nennen, als ihn "Joe" zu nennen - beide Namen haben die Aufgabe, "eine Person zu identifizieren". Ein URI ist nichts anderes als ein universell einzigartiger Name.

In RESTs Augen darüber streiten, ob ?id=["101404","7267261"]es ruhiger als ?id=101404,7267261oder \Product\101404,7267261etwas sinnlos ist.

Allerdings kann die Erstellung von URIs in der Regel häufig als guter Indikator für andere Probleme in einem RESTful-Service dienen. Es gibt ein paar rote Fahnen in Ihren URIs und Fragen im Allgemeinen.

Vorschläge

  1. Mehrere URIs für dieselbe Ressource und Content-Location

    Wir möchten vielleicht beide Stile akzeptieren, aber verursacht diese Flexibilität tatsächlich mehr Verwirrung und Kopfschmerzen (Wartbarkeit, Dokumentation usw.)?

    URIs identifizieren Ressourcen. Jede Ressource sollte einen kanonischen URI haben. Dies bedeutet nicht, dass nicht zwei URIs auf dieselbe Ressource verweisen können, aber es gibt genau definierte Möglichkeiten, dies zu tun. Wenn Sie sich für die Verwendung des JSON- und des Listenformats (oder eines anderen Formats) entscheiden, müssen Sie entscheiden, welches dieser Formate der kanonische Haupt- URI ist. Alle Antworten auf andere URIs, die auf dieselbe "Ressource" verweisen, sollten den Content-LocationHeader enthalten .

    Bei der Namensanalogie zu bleiben, mehrere URIs zu haben, ist wie Spitznamen für Menschen zu haben. Es ist vollkommen akzeptabel und oftmals sehr praktisch. Wenn ich jedoch einen Spitznamen verwende, möchte ich wahrscheinlich immer noch den vollständigen Namen wissen - die "offizielle" Art, auf diese Person zu verweisen. Wenn jemand jemanden mit seinem vollständigen Namen "Nichloas Telsa" erwähnt, weiß ich, dass er über dieselbe Person spricht, die ich als "Nick" bezeichne.

  2. "Suchen" in Ihrer URI

    Ein komplexerer Fall ist, wenn wir komplexere Eingaben anbieten möchten. Zum Beispiel, wenn wir mehrere Filter bei der Suche zulassen möchten ...

    Eine allgemeine Faustregel von mir ist, wenn Ihre URI ein Verb enthält, kann dies ein Hinweis darauf sein, dass etwas nicht stimmt. URIs identifizieren eine Ressource, sollten jedoch nicht angeben, was wir mit dieser Ressource tun. Das ist die Aufgabe von HTTP oder in erholsamen Worten unsere "einheitliche Schnittstelle".

    Um die Namensanalogie tot zu schlagen, ist die Verwendung eines Verbs in einer URI wie das Ändern des Namens einer Person, wenn Sie mit ihnen interagieren möchten. Wenn ich mit Bob interagiere, wird Bobs Name nicht zu "BobHi", wenn ich ihm Hallo sagen möchte. Wenn wir Produkte "durchsuchen" möchten, sollte sich unsere URI-Struktur nicht von "/ Product / ..." in "/ Search / ..." ändern.

Beantwortung Ihrer ersten Frage

  1. In Bezug auf ["101404","7267261"]vs 101404,7267261: Mein Vorschlag hier ist, der Einfachheit halber die JSON-Syntax zu vermeiden (dh, Ihre Benutzer müssen keine URL-Codierung durchführen, wenn Sie dies nicht wirklich müssen). Dadurch wird Ihre API ein bisschen benutzerfreundlicher. Besser noch, wie andere empfohlen haben, wählen Sie das Standardformat application/x-www-form-urlencoded, da es Ihren Endbenutzern wahrscheinlich am vertrautesten ist (z ?id[]=101404&id[]=7267261. B. ). Es ist vielleicht nicht "hübsch", aber hübsche URIs bedeuten nicht unbedingt verwendbare URIs. Um jedoch meinen Ausgangspunkt zu wiederholen, spielt es letztendlich keine Rolle, wenn ich über REST spreche. Verweile nicht zu sehr damit.

  2. Ihr komplexes Such-URI-Beispiel kann auf die gleiche Weise wie Ihr Produktbeispiel gelöst werden. Ich würde empfehlen, das application/x-www-form-urlencodedFormat erneut zu verwenden, da es bereits ein Standard ist, mit dem viele vertraut sind. Außerdem würde ich empfehlen, die beiden zusammenzuführen.

Ihre URI ...

/Search?term=pumas&filters={"productType":["Clothing","Bags"],"color":["Black","Red"]}    

Ihre URI nach der URI-Codierung ...

/Search?term=pumas&filters=%7B%22productType%22%3A%5B%22Clothing%22%2C%22Bags%22%5D%2C%22color%22%3A%5B%22Black%22%2C%22Red%22%5D%7D

Kann umgewandelt werden in ...

/Product?term=pumas&productType[]=Clothing&productType[]=Bags&color[]=Black&color[]=Red

Abgesehen davon, dass die URL-Codierung nicht erforderlich ist und die Dinge etwas standardisierter aussehen, wird die API jetzt ein wenig homogenisiert. Der Benutzer weiß, dass er an /Product/...URIs interessiert ist, wenn er ein Produkt oder eine Produktliste abrufen möchte (beide werden in RESTful-Begriffen als eine einzige "Ressource" betrachtet) .


67
Wollte nachverfolgen und beachten, dass die []Syntax nicht immer unterstützt wird (und obwohl sie häufig vorkommt, sogar gegen die URI-Spezifikation verstoßen kann). Einige HTTP-Server und Programmiersprachen bevorzugen es, nur den Namen zu wiederholen (z productType=value1&productType=value2. B. ).
Nategood

1
Die erste Frage mit dieser Abfrage .. "/ Search? Term = pumas & filter = {" productType ": [" Kleidung "," Taschen "]," Farbe ": [" Schwarz "," Rot "]}" übersetzt in ... (Produkttyp == Kleidung || Produkttyp == Taschen) && (Farbe == Schwarz || Farbe == Rot) ABER IHRE LÖSUNG: / Produkt? Begriff = Pumas & Produkttyp [] = Kleidung & Produkttyp [] = Taschen & Farbe [] = Schwarz & Farbe [] = Rot scheint zu übersetzen in ... Entweder (Produkttyp == Kleidung || Produkttyp == Taschen || Farbe == Schwarz || Farbe == Rot) oder Entweder (Produkttyp == Kleidung && Produkttyp == Taschen && Farbe == schwarz && Farbe == rot) Das scheint mir ein bisschen anders zu sein. Oder habe ich falsch verstanden?
Thomas Cheng

2
Was ist mit Eingaben in der Post-Anfrage? Ich wollte wissen, ob wir eine Ressource aktualisieren. Dann ist es eine schlechte Praxis, die Abfrage / den Filter und die Daten im Hauptteil in einem Standardformat zu senden. für zB. Wenn ich die Daten in Bezug auf den Benutzer über die API /user/und im Body ändern möchte, sende ich { q:{}, d: {} }mit qals Abfrage von mit dem Benutzer wird in der DB abgefragt und dals geänderte Daten.
Molekül

1
Was machen Sie, wenn die Liste sehr groß sein kann? Die URI ist je nach Browser in der Länge begrenzt. Ich habe normalerweise zu einer Post-Anfrage gewechselt und die Liste im Text gesendet. Irgendwelche Vorschläge dort?
Troy Cosentino

4
Es müsste SEHR groß sein (siehe stackoverflow.com/questions/417142/… ), aber ja, in den extremsten Fällen müssen Sie möglicherweise den Hauptteil der Anforderung verwenden. Das Posten von Abfragen zum Abrufen von Daten ist eines der Dinge, über die RESTafarians gerne diskutieren.
Nategood

233

Die Standardmethode zum Übergeben einer Liste von Werten als URL-Parameter besteht darin, sie zu wiederholen:

http://our.api.com/Product?id=101404&id=7267261

Der meiste Servercode interpretiert dies als eine Liste von Werten, obwohl viele Einzelwertvereinfachungen aufweisen, sodass Sie möglicherweise nachsehen müssen.

Begrenzte Werte sind ebenfalls in Ordnung.

Wenn Sie JSON an den Server senden müssen, wird es mir nicht gerne in der URL angezeigt (die ein anderes Format hat). Insbesondere haben URLs eine Größenbeschränkung (in der Praxis, wenn nicht theoretisch).

Ich habe gesehen, wie einige eine komplizierte Abfrage RESTfully ausführen, und zwar in zwei Schritten:

  1. POST Ihre Abfrageanforderungen, Erhalt einer ID (im Wesentlichen Erstellen einer Suchkriterienressource)
  2. GET die Suche unter Bezugnahme auf die obige ID
  3. Optional können Sie die Abfrageanforderungen bei Bedarf LÖSCHEN. Beachten Sie jedoch, dass diese Anforderungen zur Wiederverwendung verfügbar sind.

8
Danke Kathy. Ich glaube, ich bin bei dir und mag es nicht, JSON auch in der URL zu sehen. Ich bin jedoch kein Fan davon, einen Beitrag für eine Suche zu verfassen, die eine inhärente GET-Operation ist. Können Sie ein Beispiel dafür nennen?
Whatupwilly

1
Wenn die Abfragen als einfache Parameter funktionieren können, tun Sie dies. Die Quelle war von der Rest- Discount
Kathy Van Stone

2
Wenn Sie nur zwei Ressourcen zeigen möchten, ist James Westgates Antwort typischer
Kathy Van Stone

Dies ist die richtige Antwort. Ich bin mir sicher, dass wir in naher Zukunft einige filter = id in (a, b, c, ...) sehen werden, die von OData oder ähnlichem unterstützt werden.
Bart Calixto

So funktioniert Akka HTTP
Joan

20

Zuerst:

Ich denke, Sie können es auf zwei Arten tun

http://our.api.com/Product/<id> : wenn Sie nur einen Datensatz möchten

http://our.api.com/Product : wenn Sie alle Datensätze wollen

http://our.api.com/Product/<id1>,<id2> : wie von James vorgeschlagen, kann eine Option sein, da das, was nach dem Product-Tag kommt, ein Parameter ist

Oder die, die mir am besten gefällt, ist:

Sie können die Eigenschaft Hypermedia als Engine of Application State (HATEOAS) eines RestFul WS verwenden und einen Aufruf http://our.api.com/Productausführen, der die entsprechenden URLs von zurückgeben sollhttp://our.api.com/Product/<id> und diese anschließend aufrufen soll.

Zweite

Wenn Sie Abfragen zu den URL-Aufrufen durchführen müssen. Ich würde vorschlagen, HATEOAS wieder zu verwenden.

1) Rufen Sie an http://our.api.com/term/pumas/productType/clothing/color/black

2) Rufen Sie an http://our.api.com/term/pumas/productType/clothing,bags/color/black,red

3) (Verwenden von HATEOAS) Rufen Sie ` http://our.api.com/term/pumas/productType/ an -> erhalten Sie die URLs aller möglichen Kleidungsstücke -> rufen Sie die gewünschten an (Kleidung und Taschen) - > Erhalte die möglichen Farb-URLs -> rufe die gewünschten an


1
Ich war vor einigen Tagen in eine ähnliche Situation geraten. Ich musste eine (HATEOAS) Rest-API einstellen, um eine gefilterte (große) Liste von Objekten zu erhalten, und ich habe gerade Ihre zweite Lösung ausgewählt. Ist es nicht etwas übertrieben, sich immer wieder an die API zu erinnern?
Samson

Es hängt wirklich von Ihrem System ab ... Wenn es ein einfaches System mit wenigen "Optionen" ist, sollte es wahrscheinlich übertrieben sein. Wenn Sie jedoch einige wirklich große Listen haben, kann es sehr mühsam werden, alles in einem großen Aufruf zu erledigen. Wenn Ihre API öffentlich ist, kann dies für die Benutzer kompliziert werden (wenn es sich um eine private API handelt, sollte es einfacher sein ... Bringen Sie einfach den Benutzern bei, die Sie kennen. Alternativ können Sie sowohl den Stil, den HATEOAS als auch einen "nicht erholsamen Array" -Aufruf für fortgeschrittene Benutzer implementieren
Diego Dias,

Ich baue einen erholsamen API-Webservice in Rails auf und muss der gleichen URL-Struktur wie oben folgen ( our.api.com/term/pumas/productType/clothing/color/black ). Ich bin mir jedoch nicht sicher, wie ich die Routen entsprechend konfigurieren soll.
Rubyist

Sind Begriff, Produkttyp und Farbe Ihre Controller? Wenn ja, müssen Sie nur tun: Ressourcen: Begriff tun Ressourcen: Produkttyp tun Ressourcen: Farbe Ende Ende
Diego Dias

Produkttyp und Farbe sind die Parameter. Die Parameter von productType sind also Kleidung und die Parameter von Kleidung sind Schwarz
Rubinist

12

Vielleicht möchten Sie RFC 6570 auschecken . Diese URI-Vorlagenspezifikation zeigt viele Beispiele dafür, wie URLs Parameter enthalten können.


1
Abschnitt 3.2.8 scheint zutreffend zu sein. Obwohl es erwähnenswert ist, dass dies nur ein vorgeschlagener Standard ist und nicht über diesen Punkt hinausgegangen zu sein scheint.
Mike Post

3
@MikePost Nachdem die IETF einen zweistufigen Reifeprozess für "Standards Track" -Dokumente eingeführt hat, erwarte ich, dass 6570 noch einige Jahre so bleibt, bevor ich zu einem "Internet Standard" übergehe. tools.ietf.org/html/rfc6410 Die Spezifikation ist extrem stabil, hat viele Implementierungen und ist weit verbreitet.
Darrel Miller

Ah, ich war mir dieser Veränderung nicht bewusst. (Oder bis IETF ist jetzt vernünftiger.) Danke!
Mike Post

8

Erster Fall:

Eine normale Produktsuche würde so aussehen

http://our.api.com/product/1

Ich denke also, dass die beste Vorgehensweise für Sie wäre, dies zu tun

http://our.api.com/Product/101404,7267261

Zweiter Fall

Suche mit Querystring-Parametern - gut so. Ich wäre versucht, Begriffe mit UND und ODER zu kombinieren, anstatt sie zu verwenden[] .

PS Dies kann subjektiv sein, also tun Sie, womit Sie sich wohl fühlen.

Der Grund für das Einfügen der Daten in die URL besteht darin, dass der Link auf einer Website eingefügt / zwischen Benutzern geteilt werden kann. Wenn dies kein Problem ist, verwenden Sie stattdessen auf jeden Fall einen JSON / POST.

BEARBEITEN: Ich denke, dieser Ansatz passt zu einer Entität mit einem zusammengesetzten Schlüssel, aber nicht zu einer Abfrage für mehrere Entitäten.


3
In beiden Beispielen sollte das Trailing natürlich /nicht vorhanden sein, da der URI eine Ressource und keine Sammlung adressiert.
Lawrence Dol

2
Ich dachte immer, dass die HTTP-Verben in einer REST-Verwendung bestimmte Aktionen ausführen sollten, und dies war die Richtlinienzeile: GET: Objekt abrufen / lesen, POST-Objekt erstellen, PUT vorhandenes Objekt aktualisieren und LÖSCHEN ein Objekt löschen. Ich würde also keinen POST verwenden, um etwas abzurufen. Wenn ich eine bestimmte Objektliste (Filter) möchte, würde ich ein GET mit einer Liste in URL-Parametern durchführen (durch ein Komma getrennt scheint gut zu sein)
Alex

1

Ich werde mich der Antwort von nategood anschließen, da sie vollständig ist und Ihren Bedürfnissen zu entsprechen schien. Ich möchte jedoch einen Kommentar zur Identifizierung mehrerer (1 oder mehr) Ressourcen auf diese Weise hinzufügen:

http://our.api.com/Product/101404,7267261

Dabei haben Sie:

Komplexisieren Sie die Clients, indem Sie sie dazu zwingen, Ihre Antwort als Array zu interpretieren. Dies ist für mich nicht intuitiv, wenn ich die folgende Anfrage stelle:http://our.api.com/Product/101404

Erstellen Sie redundante APIs mit einer API zum Abrufen aller Produkte und der obigen zum Abrufen von 1 oder mehreren. Da Sie einem Benutzer aus Gründen von UX nicht mehr als eine Seite mit Details anzeigen sollten, wäre es meines Erachtens nutzlos und würde nur zum Filtern der Produkte verwendet, mehr als eine ID zu haben.

Es ist vielleicht nicht so problematisch, aber Sie müssen dies entweder selbst serverseitig behandeln, indem Sie eine einzelne Entität zurückgeben (indem Sie überprüfen, ob Ihre Antwort eine oder mehrere enthält) oder Clients sie verwalten lassen.

Beispiel

Ich möchte ein Buch bei Amazing bestellen . Ich weiß genau, um welches Buch es sich handelt, und ich sehe es in der Liste, wenn ich nach Horrorbüchern navigiere:

  1. 10 000 erstaunliche Zeilen, 0 erstaunlicher Test
  2. Die Rückkehr des erstaunlichen Monsters
  3. Lassen Sie uns erstaunlichen Code duplizieren
  4. Der erstaunliche Anfang vom Ende

Nachdem ich das zweite Buch ausgewählt habe, werde ich zu einer Seite weitergeleitet, auf der der Buchteil einer Liste aufgeführt ist:

--------------------------------------------
Book #1
--------------------------------------------
    Title: The return of the amazing monster
    Summary:
    Pages:
    Publisher:
--------------------------------------------

Oder auf einer Seite, die mir nur die vollständigen Details dieses Buches gibt?

---------------------------------
The return of the amazing monster
---------------------------------
Summary:
Pages:
Publisher:
---------------------------------

Meine Meinung

Ich würde vorschlagen, die ID in der Pfadvariablen zu verwenden, wenn die Einheitlichkeit beim Abrufen der Details dieser Ressource garantiert ist. In den folgenden APIs werden beispielsweise mehrere Möglichkeiten vorgeschlagen, um die Details für eine bestimmte Ressource abzurufen (vorausgesetzt, ein Produkt hat eine eindeutige ID und eine Spezifikation für dieses Produkt hat einen eindeutigen Namen und Sie können von oben nach unten navigieren):

/products/{id}
/products/{id}/specs/{name}

In dem Moment, in dem Sie mehr als eine Ressource benötigen, würde ich vorschlagen, aus einer größeren Sammlung zu filtern. Für das gleiche Beispiel:

/products?ids=

Dies ist natürlich meine Meinung, da es nicht auferlegt wird.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.