Wann verwende ich Pfadparameter im Vergleich zu Abfrageparametern in einer RESTful-API?

139

Ich möchte meine RESTful-API sehr vorhersehbar machen. Was ist die beste Vorgehensweise, um zu entscheiden, wann eine Segmentierung von Daten mithilfe des URI und nicht mithilfe von Abfrageparametern vorgenommen werden soll?

Für mich ist es sinnvoll, dass Systemparameter, die Paginierung, Sortierung und Gruppierung unterstützen, nach dem '?' Aber was ist mit Feldern wie "Status" und "Region" oder anderen Attributen, die Ihre Sammlung segmentieren? Wenn dies auch Abfrageparameter sein sollen, wie lautet die Faustregel, wenn Sie wissen, wann Pfadparameter verwendet werden sollen?

cosbor11
quelle
1
Eine ähnliche Frage wird hier beantwortet ... stackoverflow.com/questions/3198492/…
Lalit Mehra

Antworten:

237

Die bewährte Methode für das RESTful-API-Design besteht darin, dass Pfadparameter zum Identifizieren einer bestimmten Ressource oder von bestimmten Ressourcen verwendet werden, während Abfrageparameter zum Sortieren / Filtern dieser Ressourcen verwendet werden.

Hier ist ein Beispiel. Angenommen, Sie implementieren RESTful-API-Endpunkte für eine Entität namens Car. Sie würden Ihre Endpunkte folgendermaßen strukturieren:

GET /cars
GET /cars/:id
POST /cars
PUT /cars/:id
DELETE/cars/:id

Auf diese Weise verwenden Sie nur Pfadparameter, wenn Sie angeben, welche Ressource abgerufen werden soll. Dadurch werden die Ressourcen jedoch in keiner Weise sortiert / gefiltert.

Angenommen, Sie möchten die Möglichkeit hinzufügen, die Autos in Ihren GET-Anforderungen nach Farbe zu filtern. Da Farbe keine Ressource ist (es ist eine Eigenschaft einer Ressource), können Sie einen Abfrageparameter hinzufügen, der dies tut. Sie würden diesen Abfrageparameter wie folgt zu Ihrer GET-/cars Anforderung hinzufügen :

BEKOMMEN /cars?color=blue

Dieser Endpunkt würde so implementiert, dass nur blaue Autos zurückgegeben würden.

In Bezug auf die Syntax sollten Ihre URL-Namen nur in Kleinbuchstaben geschrieben sein. Wenn Sie einen Entitätsnamen haben, der im Allgemeinen aus zwei englischen Wörtern besteht, würden Sie einen Bindestrich verwenden, um die Wörter zu trennen, nicht die Groß- und Kleinschreibung.

Ex. /two-words

Mike
quelle
3
Vielen Dank für Ihre Antwort Mike. Dies ist eine klare und einfache Methode. eine gute Stimme von mir wert. Trotzdem entscheiden sich Entwickler häufig für den Ansatz "Autos / Blau". Ich frage mich, was ihre Gründe dafür sind. Vielleicht entscheiden sie sich dafür, Pfadparameter für Felder zu erstellen, die obligatorisch sind, oder sie tun dies, um dies anzuzeigen Die Datenbank wird von diesem Shard partitioniert.
cosbor11
1
Ich bin mir nicht sicher, was ihre Argumentation ist. Ehrlich gesagt bin ich damit nicht einverstanden. Ich denke, Konventionen zu befolgen und es einfach zu halten, ist am sinnvollsten. Auf diese Weise können die Benutzer Ihrer API besser verstehen, was sie tun müssen, um auf die Funktionalität zuzugreifen.
Mike
3
Was ist mit / Autos? ID = 1 & Farbe = Blau anstelle von Autos / 1 /? Farbe = Blau. Sie filtern im Grunde Autos Ressourcen in jedem Szenario
mko
1
Falsch, da es nur ein Auto mit der ID 1 gibt, aber Autos mit der Farbe Blau vielleicht viele. Es gibt die Unterscheidung zwischen Identität und Filter
Paul
1
Meine Hypothese, warum die Verwendung von Pfadparametern so weit verbreitet ist, ist, dass viele Entwickler aus Frameworks gelernt haben, die von Leuten entwickelt wurden, die die REST-Prinzipien nicht gut verstanden haben (insbesondere Ruby on Rails).
Chris Broski
58

Die grundlegende Art, über dieses Thema nachzudenken, ist wie folgt:

Ein URI ist eine Ressourcenkennung, die eine bestimmte Instanz eines Ressourcentyps eindeutig identifiziert. Wie alles andere im Leben hat jedes Objekt (das eine Instanz eines Typs ist) eine Reihe von Attributen, die entweder zeitinvariant oder zeitlich sind.

Im obigen Beispiel ist ein Auto ein sehr greifbares Objekt mit Attributen wie Marke, Modell und Fahrgestellnummer, die sich nie ändern, sowie Farbe, Federung usw., die sich im Laufe der Zeit ändern können. Wenn wir also den URI mit Attributen codieren, die sich im Laufe der Zeit (zeitlich) ändern können, erhalten wir möglicherweise mehrere URIs für dasselbe Objekt:

GET /cars/honda/civic/coupe/{vin}/{color=red}

Und Jahre später, wenn die Farbe dieses Autos in Schwarz geändert wird:

GET /cars/honda/civic/coupe/{vin}/{color=black}

Beachten Sie, dass sich die Autoinstanz selbst (das Objekt) nicht geändert hat - es ist nur die Farbe, die sich geändert hat. Wenn mehrere URIs auf dieselbe Objektinstanz verweisen, müssen Sie mehrere URI-Handler erstellen. Dies ist kein effizientes Design und natürlich nicht intuitiv.

Daher sollte der URI nur aus Teilen bestehen, die sich niemals ändern und diese Ressource während ihrer gesamten Lebensdauer eindeutig identifizieren. Alles, was sich ändern kann, sollte als solches für Abfrageparameter reserviert werden:

GET /cars/honda/civic/coupe/{vin}?color={black}

Fazit: Denken Sie an Polymorphismus.

Kingz
quelle
2
Interessantes Paradigma. Ist dies ein häufig verwendetes Designmuster? Können Sie einige APIs bereitstellen, die dies in ihrer Dokumentation verwenden, oder einige Referenzen, die diese Strategie beschreiben?
cosbor11
1
Mir gefällt, wie Sie "TYP" hervorgehoben haben, als Sie "Ein URI ist eine Ressourcen-ID, die eine bestimmte Instanz eines Ressourcentyps eindeutig identifiziert" geschrieben haben. Ich denke, das ist ein wichtiger Unterschied.
Jrahhali
15

In einer REST-API sollten Sie nicht übermäßig von vorhersehbaren URIs betroffen sein. Der bloße Vorschlag der URI-Vorhersagbarkeit spielt auf ein Missverständnis der RESTful-Architektur an. Es wird davon ausgegangen, dass ein Client selbst URIs erstellt, die er eigentlich nicht haben sollte.

Ich gehe jedoch davon aus, dass Sie keine echte REST-API erstellen, sondern eine von REST inspirierte API (wie die von Google Drive). In diesen Fällen lautet die Faustregel "Pfadparameter = Ressourcenidentifikation" und "Abfrageparameter = Ressourcensortierung". Es stellt sich also die Frage, ob Sie Ihre Ressource OHNE Status / Region eindeutig identifizieren können. Wenn ja, dann ist es vielleicht ein Abfrageparameter. Wenn nein, dann ist es ein Pfadparameter.

HTH.

Oliver McPhee
quelle
11
Ich bin anderer Meinung, eine gute API sollte vorhersehbar sein. RUHIG oder anders.
cosbor11
3
Ich glaube schon. Es sollte einen Reim und einen Grund für die Bildung des URI geben, anstatt Endpunkte willkürlich zu benennen. Wenn man einen API-Client intuitiv schreiben kann, ohne ständig auf die Dokumentation zu verweisen, haben Sie meiner Meinung nach eine gute API geschrieben.
cosbor11
2
"Wenn man einen API-Client intuitiv schreiben kann, ohne ständig auf die Dokumentation zu verweisen". Hier ist meines Erachtens unser Verständnis von REST unterschiedlich ... der API-Client sollte niemals eine URL "erstellen" müssen. Sie sollten es aus der Antwort des vorherigen API-Aufrufs auswählen. Wenn Sie eine Website als Analogie nehmen ... Gehen Sie zu facebook.com und wählen Sie einen Link zur Ereignisseite. Es ist Ihnen egal, ob die URL für Facebook-Ereignisse "vorhersehbar" ist, da Sie sie nicht eingeben. Sie gelangen über Hypermedia-Links dorthin. Gleiches gilt für eine REST-API. Machen Sie URIs für Sie (den Server) sinnvoll, aber nicht für den Client
Oliver McPhee
2
Hinweis hinzugefügt. Dies bedeutet nicht, dass die URIs keinem leicht verständlichen Muster folgen sollten, sondern nur, dass dies keine Einschränkung einer RESTful-API ist. Das größte Problem in diesem Bereich ist die Annahme, dass ein Client die URLs selbst erstellen sollte. Sie sollten nicht, da dies eine Kopplung zwischen Client und Server schafft, die nicht existieren sollte. (zB - der Server kann dann keine URL ändern, ohne alle Clientanwendungen zu beschädigen). In einer REST-API kann der Server sie nach Belieben ändern.
Oliver McPhee
3
+1 für die Verwendung der folgenden Wörter: "'Pfadparameter = Ressourcenidentifikation' und 'Abfrageparameter = Ressourcensortierung'". Das hat es wirklich für mich geklärt.
Doug
3

Einmal habe ich eine API entworfen, welche Hauptressource war people. Normalerweise peopleforderten Benutzer gefiltert an , um zu verhindern, dass Benutzer so etwas wie /people?settlement=urbanjedes Mal aufrufen. Ich implementierte dies, /people/urbanwas mir später das einfache Hinzufügen ermöglichte /people/rural. Dies ermöglicht auch den Zugriff auf die vollständige /peopleListe, falls diese später von Nutzen sein sollte. Kurz gesagt, meine Argumentation war, einen Pfad zu allgemeinen Teilmengen hinzuzufügen

Von hier aus :

Aliase für häufig gestellte Fragen

Um die API-Erfahrung für den Durchschnittsverbraucher angenehmer zu gestalten, sollten Sie Bedingungen in leicht zugängliche RESTful-Pfade packen. Beispielsweise könnte die oben geschlossene Ticketabfrage oben als verpackt werdenGET /tickets/recently_closed

Mario Gil
quelle
1

Im Allgemeinen tendiere ich dazu, Pfadparameter zu verwenden, wenn die Ressource eine offensichtliche "Hierarchie" aufweist, wie z.

/region/state/42

Wenn diese einzelne Ressource einen Status hat, könnte man:

/region/state/42/status

Wenn 'region' jedoch nicht wirklich Teil der offengelegten Ressource ist, gehört es wahrscheinlich zu einem der Abfrageparameter - ähnlich wie bei der Paginierung (wie Sie erwähnt haben).

morsor
quelle
0

Die Segmentierung ist hierarchischer und "hübscher", kann jedoch einschränkend sein.

Wenn Sie beispielsweise eine URL mit drei Segmenten haben, die jeweils unterschiedliche Parameter übergeben, um über Marke, Modell und Farbe nach einem Auto zu suchen:

www.example.com/search/honda/civic/blue

Dies ist eine sehr hübsche URL, an die sich der Endbenutzer leichter erinnern kann, aber jetzt bleibt Ihre Art bei dieser Struktur. Angenommen, Sie möchten es so gestalten, dass der Benutzer bei der Suche nach ALLEN blauen Autos oder ALLEN Honda Civics suchen kann? Ein Abfrageparameter löst dies, weil er ein Schlüsselwertpaar ergibt. Sie könnten also bestehen:

www.example.com/search?color=blue
www.example.com/search?make=civic

Jetzt haben Sie die Möglichkeit, den Wert über seinen Schlüssel zu referenzieren - entweder "color" oder "make" in Ihrem Abfragecode.

Sie könnten dies umgehen, indem Sie möglicherweise mehr Segmente verwenden, um eine Art Schlüsselwertstruktur zu erstellen, wie:

www.example.com/search/make/honda/model/civic/color/blue

Hoffe das macht Sinn ..

webmaster_sean
quelle
-2

Beispiel-URL: /rest/{keyword}

Diese URL ist ein Beispiel für Pfadparameter. Wir können diese URL-Daten mithilfe von erhalten @PathParam.

Beispiel-URL: /rest?keyword=java&limit=10

Diese URL ist ein Beispiel für Abfrageparameter. Wir können diese URL-Daten mithilfe von erhalten @Queryparam.

Sujithrao
quelle