it-swarm.com.de

REST API Best Practice: Akzeptieren einer Liste von Parameterwerten als Eingabe

Wir starten eine neue REST -API und möchten Community-Informationen zu bewährten Methoden zur Formatierung von Eingabeparametern erhalten:

Momentan ist unsere API sehr JSON-zentriert (gibt nur JSON zurück). Die Debatte darüber, ob wir XML zurückgeben wollen/müssen, ist eine separate Frage.

Da unsere API-Ausgabe JSON-zentriert ist, haben wir einen Pfad beschritten, in dem unsere Eingaben ein bisschen JSON-zentriert sind, und ich habe gedacht, dass dies für einige nützlich sein könnte, aber im Allgemeinen seltsam.

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

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

Sollten wir dies vereinfachen als:

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

Oder ist 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 wollen. 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"]}

Die Filtertypen (z. B. productType und color) müssen nicht unbedingt wie folgt als Anforderungsnamen angegeben werden:

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

Weil wir alle Filtereingänge zusammenfassen wollten.

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

Ich weiß, dass unsere JavaScript-Clients, die AJAX Aufrufe an die API ausführen, die JSON-Eingaben zu schätzen wissen, um ihr Leben zu vereinfachen.

373
whatupwilly

Einen Schritt zurück

In erster Linie beschreibt REST einen URI als eine universell eindeutige ID. Viel zu viele Menschen sind mit der Struktur von URIs beschäftigt und welche URIs "ruhiger" sind als andere. Dieses Argument ist so lächerlich, als würde man sagen, jemandem den Namen "Bob" zu geben, ist besser als ihm den Namen "Joe" zu geben - beide Namen erledigen die Aufgabe, eine Person zu identifizieren. Ein URI ist nichts mehr als ein universell eindeutigerName.

In den Augen von REST ist es also vergeblich, darüber zu streiten, ob _?id=["101404","7267261"]_ ruhiger ist als _?id=101404,7267261_ oder _\Product\101404,7267261_.

Allerdings kann die Art und Weise, wie URIs erstellt werden, in der Regel ein guter Indikator für andere Probleme in einem RESTful-Service sein. 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 den kanonischen URI one haben. Dies bedeutet nicht, dass nicht zwei URIs auf dieselbe Ressource verweisen können , aberes gibt genau definierte Möglichkeiten, dies zu tun. Wenn Sie sich dafür entscheiden, sowohl das JSON- als auch das listenbasierte Format (oder ein anderes Format) zu verwenden, müssen Sie entscheiden, welches dieser Formate der kanonischeURI ist. Alle Antworten auf andere URIs, die auf dieselbe "Ressource" verweisen, sollten den Header Content-Location enthalten.

    Bei der Namensanalogie zu bleiben, mehrere URIs zu haben, ist wie Spitznamen für Menschen zu haben. Es ist vollkommen akzeptabel und oft sehr praktisch, aber wenn ich einen Spitznamen verwende, möchte ich wahrscheinlich immer noch deren vollständigen Namen kennen - die "offizielle" Art, sich auf diese Person zu beziehen. Wenn jemand jemanden mit seinem vollständigen Namen "Nichloas Telsa" erwähnt, weiß ich, dass es sich um dieselbe Person handelt, die ich als "Nick" bezeichne.

  2. "Suchen" in Ihrer URI

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

    Eine allgemeine Faustregel ist: Wenn Ihre URI ein Verb enthält, kann dies ein Hinweis darauf sein, dass etwas nicht stimmt. URIs identifizieren eine Ressource, sie sollten jedoch nicht angeben, waswir mit dieser Ressource tun. Das ist die Aufgabe von HTTP oder in aller Ruhe unserer "einheitlichen Schnittstelle".

    Das Verwenden eines Verbs in einer URI bedeutet, den Namen einer Person zu ändern, wenn Sie mit ihr 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 (d. H. Ihre Benutzer müssen keine URL-Codierung durchführen, wenn Sie dies nicht wirklich müssen). Dadurch wird Ihre API ein wenig benutzerfreundlicher. Besser noch, wie andere empfohlen haben, verwenden Sie das Standardformat _application/x-www-form-urlencoded_, da es Ihren Endbenutzern wahrscheinlich am vertrautesten ist (z. B. _?id[]=101404&id[]=7267261_). Es ist möglicherweise nicht "hübsch", aber hübsche URIs bedeuten nicht unbedingt verwendbare URIs. Um jedoch meinen anfänglichen Punkt zu wiederholen, spielt es letztendlich keine Rolle, wenn ich über REST spreche. Bleib nicht zu lange dran.

  2. Ihr komplexes Such-URI-Beispiel kann genauso gelöst werden wie Ihr Produktbeispiel. Ich würde empfehlen, das _application/x-www-form-urlencoded_ -Format 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 keine URL-Kodierung 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 Liste von Produkten abrufen möchte (beide werden in RESTful-Begriffen als eine einzige "Ressource" betrachtet).

321
nategood

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 Vereinfachungen für einzelne Werte aufweisen, so dass Sie möglicherweise nachsehen müssen.

Begrenzte Werte sind ebenfalls in Ordnung.

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

Die Art und Weise, wie ich einige RESTfully gesehen habe, ist in zwei Schritten:

  1. POST Ihre Abfrageanforderungen, die eine ID zurückerhalten (im Wesentlichen das Erstellen einer Suchkriterienressource)
  2. GET die Suche unter Bezugnahme auf die oben angegebene ID
  3. optional können Sie die Abfrageanforderungen bei Bedarf LÖSCHEN. Beachten Sie jedoch, dass diese Anforderungen zur Wiederverwendung verfügbar sind.
213
Kathy Van Stone

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 möchten

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

Oder das, was mir am besten gefällt, ist:

Sie können die Hypermedia als die Engine-of-Application-State-Eigenschaft (HATEOAS) eines RestFul-WS verwenden und http://our.api.com/Product aufrufen, das zurückgegeben werden soll die entsprechenden URLs von http://our.api.com/Product/<id> und rufen sie danach auf.

Zweiter

Wenn Sie Fragen zu den URL-Aufrufen haben. Ich würde vorschlagen, wieder HATEOAS zu verwenden.

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

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

3) (mit HATEOAS) Rufen Sie an: http://our.api.com/term/pumas/productType/ > Rufen Sie die an, die Sie möchten (Kleidung und Taschen) -> Erhalten Sie die möglichen Farb-URLs -> Rufen Sie die an, die Sie möchten

19
Diego Dias

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

9
Darrel Miller

Erster Fall:

Eine normale Produktsuche würde so aussehen

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

Ich denke also, dass dies die beste Vorgehensweise für Sie ist

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

Zweiter Fall

Suche mit Querystring-Parametern - genau so. Ich wäre versucht, Begriffe mit AND und OR zu kombinieren, anstatt [] zu verwenden.

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

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

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

7
James Westgate

Ich werde mich für die Antwort von nategood einsetzen, da sie vollständig ist und Ihre Bedürfnisse zu erfüllen scheint. Ich möchte jedoch einen Kommentar hinzufügen, um mehrere (1 oder mehr) Ressourcen auf diese Weise zu identifizieren:

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

Sie tun dies:

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

Erstellen Sie redundante APIs mit einer API zum Abrufen aller Produkte und der obigen API zum Abrufen von 1 oder mehreren. Da Sie einem Benutzer aus Gründen von UX nicht mehr als eine Seite mit Details zeigen sollten, halte ich mehr als eine ID für nutzlos und würde nur zum Filtern der Produkte verwendet.

Das ist vielleicht nicht so problematisch, aber Sie müssen dies entweder selbst auf der Serverseite erledigen, indem Sie eine einzelne Entität zurückgeben (indem Sie überprüfen, ob Ihre Antwort eine oder mehrere enthält) oder die Clients dies verwalten lassen.

Beispiel

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

  1. 10 000 erstaunliche Linien, 0 erstaunlicher Test
  2. Die Rückkehr des erstaunlichen Monsters
  3. Lass 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, auf der ich nur die vollständigen Details zu diesem Buch finden kann?

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

Meine Meinung

Ich würde vorschlagen, die ID in der Pfadvariablen zu verwenden, wenn die Eindeutigkeit beim Abrufen der Details dieser Ressource garantiert ist. Die folgenden APIs bieten beispielsweise mehrere Möglichkeiten, um die Details für eine bestimmte Ressource abzurufen (vorausgesetzt, ein Produkt hat eine eindeutige ID und eine Spezifikation für dieses Produkt 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.

0
gumol