RESTful URL Design für die Suche

426

Ich suche nach einer vernünftigen Möglichkeit, Suchanfragen als RESTful-URLs darzustellen.

Das Setup: Ich habe zwei Modelle, Autos und Garagen, bei denen Autos in Garagen sein können. Meine URLs sehen also so aus:

/car/xxxx
  xxx == car id
  returns car with given id

/garage/yyy
  yyy = garage id
  returns garage with given id

Ein Auto kann alleine existieren (daher das / Auto) oder es kann in einer Garage existieren. Was ist der richtige Weg, um beispielsweise alle Autos in einer bestimmten Garage darzustellen? Etwas wie:

/garage/yyy/cars     ?

Wie wäre es mit der Vereinigung von Autos in Garage JJJ und ZZZ?

Wie kann eine Suche nach Autos mit bestimmten Attributen richtig dargestellt werden? Sprich: Zeig mir alle blauen Limousinen mit 4 Türen:

/car/search?color=blue&type=sedan&doors=4

oder sollte es stattdessen / autos sein?

Die Verwendung von "Suche" scheint dort unangemessen - was ist ein besserer Weg / Begriff? Sollte es nur sein:

/cars/?color=blue&type=sedan&doors=4

Sollten die Suchparameter Teil von PATHINFO oder QUERYSTRING sein?

Kurz gesagt, ich suche nach Anleitungen für das modellübergreifende REST-URL-Design und für die Suche.

[Update] Ich mag Justins Antwort, aber er behandelt den Mehrfeld-Suchfall nicht:

/cars/color:blue/type:sedan/doors:4

oder etwas ähnliches. Wie gehen wir weiter?

/cars/color/blue

zum Mehrfeldfall?

Parand
quelle
16
Auch wenn es aussieht , ist in Englisch besser, Mischen /carsund /carnicht semantisch ist und daher eine schlechte Idee. Verwenden Sie immer den Plural, wenn sich mehr als ein Element in dieser Kategorie befindet.
Zaz
4
Das sind schlechte Antworten. Die Suche sollte Abfragezeichenfolgen verwenden. Abfragezeichenfolgen sind bei ordnungsgemäßer Verwendung (dh für die Suche) zu 100% REST-fähig.
pbreitenbach
2
Schauen
Anatoly G

Antworten:

435

Verwenden Sie für die Suche Querystringe. Das ist vollkommen RESTful:

/cars?color=blue&type=sedan&doors=4

Ein Vorteil von regulären Abfrageringen besteht darin, dass sie Standard sind und allgemein verstanden werden und dass sie aus Form-Get generiert werden können.

pbreitenbach
quelle
42
Das ist richtig. Der springende Punkt bei Abfragezeichenfolgen ist das Suchen.
Aehlke
22
Dies ist in der Tat richtig, da gemäß RFC3986 der Pfad und der Querystring die Ressource identifizieren. Darüber hinaus wäre eine korrekte Benennung einfach /cars?color=whatever.
Lloeki
35
Was ist mit Fällen, in denen Sie Komparatoren möchten (>, <, <=,> =)? / Autos? Bewertung <= 3?
Jesse
3
Was ist, wenn Sie auf Ressourcen zugreifen möchten, die unter der Abfragezeichenfolge verschachtelt sind? ZB /cars?color=blue&type=sedan&doors=4/engineswird nicht funktionieren
Abe Voelker
9
@mjs /cars?param=valuedient zum einfachen Filtern in der Fahrzeugliste und /cars/search?param=valuezum Erstellen einer Suche (mit oder ohne Beständigkeit), bei der das Ergebnis Suchbewertungen, Kategorisierungen usw. enthalten kann. Sie können auch eine benannte Suche wie erstellen / löschen /cars/search/mysearch. Schauen Sie sich das an: stackoverflow.com/a/18933902/1480391
Yves M.
121

Beim hübschen RESTful-URL-Design geht es darum, eine Ressource basierend auf einer Struktur anzuzeigen (verzeichnisähnliche Struktur, Datum: Artikel / 2005/5/13, Objekt und seine Attribute, ..). Der Schrägstrich /zeigt eine hierarchische Struktur an-id stattdessen die.

Hierarchische Struktur

Ich persönlich würde es vorziehen:

/garage-id/cars/car-id
/cars/car-id   #for cars not in garages

Wenn ein Benutzer das /car-idTeil entfernt, wird die carsVorschau angezeigt - intuitiv. Der Benutzer weiß genau, wo er sich im Baum befindet und worauf er schaut. Er weiß vom ersten Blick an, dass Garagen und Autos in Beziehung stehen. /car-idbedeutet auch, dass es im Gegensatz zu zusammen gehört/car/id .

Suchen

Die Suchabfrage ist in Ordnung , es gibt nur Ihre Präferenz, was berücksichtigt werden sollte. Der lustige Teil kommt beim Beitreten zu Suchanfragen (siehe unten).

/cars?color=blue;type=sedan   #most prefered by me
/cars;color-blue+doors-4+type-sedan   #looks good when using car-id
/cars?color=blue&doors=4&type=sedan   #I don't recommend using &*

Oder im Grunde alles, was kein Schrägstrich ist, wie oben erklärt.
Die Formel : /cars[?;]color[=-:]blue[,;+&], * obwohl ich das &Zeichen nicht verwenden würde , da es auf den ersten Blick nicht aus dem Text erkennbar ist.

** **. Wussten Sie, dass das Übergeben eines JSON-Objekts in URI RESTful ist? ** **.

Liste der Optionen

/cars?color=black,blue,red;doors=3,5;type=sedan   #most prefered by me
/cars?color:black:blue:red;doors:3:5;type:sedan
/cars?color(black,blue,red);doors(3,5);type(sedan)   #does not look bad at all
/cars?color:(black,blue,red);doors:(3,5);type:sedan   #little difference

mögliche Funktionen?

Suchzeichenfolgen negieren (!)
So suchen Sie nach Autos, aber nicht nach Schwarz und Rot :
?color=!black,!red
color:(!black,!red)

Neues Profil Suchen
Suchen rot oder blau oder schwarz Autos mit 3 Türen in Garagen ID 1..20 oder 101..103 oder 999 , aber nicht 5 /garage[id=1-20,101-103,999,!5]/cars[color=red,blue,black;doors=3]
Sie können dann komplexere Suchanfragen konstruieren. (Schauen Sie sich die CSS3-Attributübereinstimmung an, um die Idee zum Abgleichen von Teilzeichenfolgen zu finden. Suchen Sie beispielsweise nach Benutzern, die "bar" enthalten.user*=bar .)

Fazit

Auf jeden Fall könnte dies der wichtigste Teil für Sie sein, denn Sie können es tun, wie Sie möchten. Denken Sie jedoch daran, dass RESTful URI eine Struktur darstellt, die leicht zu verstehen ist, z. B. verzeichnisartig /directory/file./collection/node/item , Daten /articles/{year}/{month}/{day}.. Und wenn man weglassen In jedem der letzten Segmente wissen Sie sofort, was Sie erhalten.

Also .., alle diese Zeichen dürfen nicht codiert werden :

  • vorbehaltlos: a-zA-Z0-9_.-~
    Regel zulässig, sowohl codiert als auch nicht, sind beide Verwendungen gleichwertig.
  • spezielle Charaktere: $-_.+!*'(),
  • reserviert: ;/?:@=&
    Kann für den von ihnen dargestellten Zweck unverschlüsselt verwendet werden, andernfalls müssen sie verschlüsselt werden.
  • unsicher: <>"#%{}|\^~[]`
    Warum unsicher und warum sollte lieber codiert werden: RFC 1738 siehe 2.2

    Weitere Zeichenklassen finden Sie auch in RFC 1738 # Seite 20 .

RFC 3986 siehe 2.2
Trotz allem, was ich zuvor gesagt habe, gibt es hier eine allgemeine Unterscheidung von Begrenzern, was bedeutet, dass einige " wichtiger " sind als andere.

  • generische Begrenzer: :/?#[]@
  • Unterbegrenzer: !$&'()*+,;=

Weitere Informationen:
Hierarchie: siehe 2.3 , siehe 1.2.3
URL-Pfadparametersyntax
CSS3-Attributübereinstimmung
IBM: RESTful Web Services - Die Grundlagen
Hinweis: RFC 1738 wurde von RFC 3986 aktualisiert

Qwerty
quelle
3
Ich glaube nicht, dass ich nicht daran gedacht habe, JSON in der Abfragezeichenfolge zu verwenden. Es ist die Antwort auf ein Problem, mit dem ich konfrontiert war - eine komplexe Suchstruktur ohne Verwendung POST. Auch andere Ideen, die Sie in Ihrer Antwort angegeben haben, sind sehr wertvoll. Vielen Dank!
Gustavohenke
4
@ Qwerty: toller Beitrag! Ich habe mich gefragt: Der einzige Grund für die Verwendung ;im Gegensatz zu &ist die Lesbarkeit? Denn wenn ja, würde ich das eigentlich bevorzugen, &da es das üblichere Trennzeichen ist ... richtig? :) Vielen Dank!
Flo
3
@Flo Ja genau :), aber denken Sie daran, dass &als Trennzeichen nur Entwicklern bekannt ist. Eltern, Großeltern und die nicht-it-gebildete Bevölkerung akzeptieren Trennzeichen, wie sie in allgemein geschriebenen Texten verwendet werden.
Qwerty
17
Warum ein nicht standardmäßiges Schema erstellen, wenn Abfragezeichenfolgen gut verstanden und standardisiert sind?
pbreitenbach
1
@Qwerty nichts hindert Sie an / search? Cars = rot, blau, grün & garages = 1,2,3 Oder wenn Sie ein <multiselect> -Formular verwenden: / search? Cars = rot & cars = blau &
garages
36

Obwohl es einige Vorteile hat, die Parameter im Pfad zu haben, gibt es IMO einige überwiegende Faktoren.

  • Nicht alle für eine Suchabfrage benötigten Zeichen sind in einer URL zulässig. Die meisten Interpunktions- und Unicode-Zeichen müssten als Abfragezeichenfolgenparameter URL-codiert sein. Ich ringe mit dem gleichen Problem. Ich möchte XPath in der URL verwenden, aber nicht alle XPath-Syntax ist mit einem URI-Pfad kompatibel. Für einfache Pfade /cars/doors/driver/lock/combinationwäre es daher angebracht, das combinationElement ' ' im XML-Dokument der Fahrertür zu lokalisieren . Ist /car/doors[id='driver' and lock/combination='1234']aber nicht so freundlich.

  • Es gibt einen Unterschied zwischen dem Filtern einer Ressource anhand eines ihrer Attribute und dem Angeben einer Ressource.

    Zum Beispiel seit

    /cars/colors Gibt eine Liste aller Farben für alle Autos zurück (die zurückgegebene Ressource ist eine Sammlung von Farbobjekten).

    /cars/colors/red,blue,green würde eine Liste von Farbobjekten zurückgeben, die rot, blau oder grün sind, keine Sammlung von Autos.

    Um Autos zurückzugeben, wäre der Weg

    /cars?color=red,blue,green oder /cars/search?color=red,blue,green

  • Parameter im Pfad sind schwieriger zu lesen, da Name / Wert-Paare nicht vom Rest des Pfads isoliert sind, bei dem es sich nicht um Name / Wert-Paare handelt.

Ein letzter Kommentar. Ich bevorzuge /garages/yyy/cars(immer Plural) /garage/yyy/cars(vielleicht war es ein Tippfehler in der ursprünglichen Antwort), weil es vermeidet, den Pfad zwischen Singular und Plural zu ändern. Bei Wörtern mit einem hinzugefügten 's' ist die Änderung nicht so schlecht, aber das Ändern /person/yyy/friendsin /people/yyyscheint umständlich zu sein.

Doug Domeny
quelle
2
Ja, ich stimme zu ... außerdem sollte die Pfadstruktur der URLs die natürlichen Beziehungen zwischen Entitäten widerspiegeln, eine Art Karte meiner Ressourcen, wie eine Garage viele Autos hat, ein Auto zu einer Garage gehört und so ... und lassen die Filterparameter, denn das ist es, worüber wir sprechen, um Que Querystring ... was denkst du?
Eröffnung
31

Um Peters Antwort zu erweitern, könnten Sie Search zu einer erstklassigen Ressource machen:

POST    /searches          # create a new search
GET     /searches          # list all searches (admin)
GET     /searches/{id}     # show the results of a previously-run search
DELETE  /searches/{id}     # delete a search (admin)

Die Suchressource enthält Felder für Farbe, Modell erstellen, Garagenstatus usw. und kann in XML, JSON oder einem anderen Format angegeben werden. Wie bei der Ressource Auto und Garage können Sie den Zugriff auf Suchvorgänge basierend auf der Authentifizierung einschränken. Benutzer, die häufig dieselben Suchvorgänge ausführen, können sie in ihren Profilen speichern, sodass sie nicht neu erstellt werden müssen. Die URLs sind so kurz, dass sie in vielen Fällen problemlos per E-Mail gehandelt werden können. Diese gespeicherten Suchvorgänge können die Grundlage für benutzerdefinierte RSS-Feeds usw. sein.

Es gibt viele Möglichkeiten, Suchen zu verwenden, wenn Sie sie als Ressourcen betrachten.

Die Idee wird in diesem Railscast näher erläutert .

Reiches Apodaca
quelle
6
widerspricht dieser Ansatz nicht der Idee, mit einem unruhigen Protokoll zu arbeiten? Ich meine, eine Suche in einer Datenbank fortzusetzen, ist eine Art zustandsbehaftete Verbindung ... nicht wahr?
Eröffnung
5
Es ist eher wie ein staatlicher Service. Außerdem ändern wir den Status des Dienstes jedes Mal, wenn wir ein neues Auto oder eine neue Garage hinzufügen. Eine Suche ist nur eine weitere Ressource, die mit allen HTTP-Verben verwendet werden kann.
Rich Apodaca
2
Wie definiert das Obige eine URI-Konvention?
Rich Apodaca
3
REST hat nichts mit hübschen URIs oder URI-Verschachtelungen usw. zu tun. Wenn Sie URIs als Teil Ihrer API definieren, handelt es sich nicht um REST.
Aehlke
2
Ich habe diesen schon einmal diskutiert. Dies ist keineswegs aussagekräftig, aber es ist eine schreckliche Sache. Das 'Löschen' der Suche ist nicht ganz klar. Hier sagen Sie, dass es diese Suchentität löscht, aber ich möchte es verwenden, um die Ergebnisse zu löschen, die ich durch diese Suche gefunden habe. Bitte fügen Sie keine Suchanfragen als Ressource hinzu.
Thecoshman
12

Justins Antwort ist wahrscheinlich der richtige Weg, obwohl es in einigen Anwendungen sinnvoll sein kann, eine bestimmte Suche als eigenständige Ressource zu betrachten, z. B. wenn Sie benannte gespeicherte Suchvorgänge unterstützen möchten:

/search/{searchQuery}

oder

/search/{savedSearchName}
Peter Hilton
quelle
11
Nein. Es macht niemals Sinn, eine Aktion als Ressource zu haben.
Thecoshman
3
@thecoshman Wie in einem Kommentar oben erwähnt, ist die Suche auch ein Substantiv.
Andho
6

Ich verwende zwei Ansätze, um Suchvorgänge zu implementieren.

1) Einfachster Fall, um zugeordnete Elemente abzufragen und zur Navigation.

    /cars?q.garage.id.eq=1

Dies bedeutet, dass Fahrzeuge mit einer Garagen-ID von 1 abgefragt werden müssen.

Es ist auch möglich, komplexere Suchvorgänge zu erstellen:

    /cars?q.garage.street.eq=FirstStreet&q.color.ne=red&offset=300&max=100

Autos in allen Garagen in FirstStreet, die nicht rot sind (3. Seite, 100 Elemente pro Seite).

2) Komplexe Abfragen werden als reguläre Ressourcen betrachtet, die erstellt werden und wiederhergestellt werden können.

    POST /searches  => Create
    GET  /searches/1  => Recover search
    GET  /searches/1?offset=300&max=100  => pagination in search

Der POST-Text für die Sucherstellung lautet wie folgt:

    {  
       "$class":"test.Car",
       "$q":{
          "$eq" : { "color" : "red" },
          "garage" : {
             "$ne" : { "street" : "FirstStreet" }
          }
       }
    }

Es basiert auf Grails (Kriterien DSL): http://grails.org/doc/2.4.3/ref/Domain%20Classes/createCriteria.html

user2108278
quelle
5

Dies ist kein REST. Sie können keine URIs für Ressourcen in Ihrer API definieren. Die Ressourcennavigation muss hypertextgesteuert sein. Es ist in Ordnung, wenn Sie hübsche URIs und viel Kopplung wünschen, aber nennen Sie es einfach nicht REST, da es direkt gegen die Einschränkungen der RESTful-Architektur verstößt.

Siehe diesen Artikel des Erfinders von REST.

aehlke
quelle
28
Sie haben Recht, dass es sich nicht um REST handelt, sondern um ein URL-Design für ein RESTful-System. Sie sagen jedoch auch zu Unrecht, dass dies gegen die RESTful-Architektur verstößt. Die Hypertext-Einschränkung von REST ist orthogonal zu einem guten URL-Design für ein RESTful-System. Ich erinnere mich, dass es vor einigen Jahren eine Diskussion mit Roy T. Fielding auf der REST-Liste gab, an der ich teilgenommen habe, wo er dies ausdrücklich angegeben hat. Anders gesagt, es ist möglich, Hypertext und URL-Design zu haben. Das URL-Design für RESTful-Systeme ist wie ein Einzug in die Programmierung. nicht erforderlich, aber eine sehr gute Idee (Python usw. ignorieren)
MikeSchinkel
2
Es tut mir leid, du hast recht. Ich hatte gerade den Eindruck vom OP, dass er die Kunden darauf aufmerksam machen würde, wie URLs erstellt werden - er würde URL-Layouts zu einem Teil seiner API machen. Das wäre eine Verletzung von REST.
Aehlke
@aehlke, du solltest deine Antwort aktualisieren, um sie deinem Kommentar anzupassen.
James McMahon
1
Es entspricht dem Richardson-Reifegradmodell der Stufe 2 . Sie beziehen sich auf Stufe 3. Akzeptieren Sie REST einfach als etwas, das schrittweise übernommen werden kann.
Jules Randolph
1
@ Jules Randolph - Entschuldigung, meine Antwort wurde nur Monate geschrieben, nachdem das Richardson-Reifegradmodell zum ersten Mal geprägt wurde und bevor Martin Fowler und andere Autoren es populär machten :) In der Tat ist es ein lehrreiches Modell, dem man folgen muss. Fühlen Sie sich frei, die Antwort zu bearbeiten.
Aehlke
1

Obwohl mir Justins Antwort gefällt, ist sie meiner Meinung nach eher ein Filter als eine Suche. Was ist, wenn ich etwas über Autos mit Namen wissen möchte, die mit cam beginnen?

So wie ich es sehe, können Sie es in den Umgang mit bestimmten Ressourcen einbauen:
/ cars / cam *

Oder Sie können es einfach in den Filter einfügen:
/ cars / door / 4 / name / cam * / colours / red, blau, grün

Ich persönlich bevorzuge letzteres, bin aber keineswegs ein Experte für REST (nachdem ich erst vor ungefähr zwei Wochen davon gehört habe ...)


quelle
So:/cars?name=cam*
DanMan
1

RESTful empfiehlt nicht, Verben in URLs zu verwenden. / Cars / search ist nicht erholsam. Der richtige Weg zum Filtern / Suchen / Paginieren Ihrer APIs sind Abfrageparameter. Es kann jedoch Fälle geben, in denen Sie die Norm brechen müssen. Wenn Sie beispielsweise über mehrere Ressourcen hinweg suchen, müssen Sie so etwas wie / search? Q = query verwenden

Unter http://saipraveenblog.wordpress.com/2014/09/29/rest-api-best-practices/ finden Sie die Best Practices für das Entwerfen von RESTful-APIs

java_geek
quelle
1
Suche ist auch ein Substantiv 😀
jith912
1

Darüber hinaus würde ich auch vorschlagen:

/cars/search/all{?color,model,year}
/cars/search/by-parameters{?color,model,year}
/cars/search/by-vendor{?vendor}

Hier Searchwird als untergeordnete Ressource der CarsRessource betrachtet.

aux
quelle
1

Hier gibt es viele gute Möglichkeiten für Ihren Fall. Sie sollten dennoch in Betracht ziehen, den POST-Body zu verwenden.

Die Abfragezeichenfolge ist perfekt für Ihr Beispiel. Wenn Sie jedoch etwas Komplizierteres haben, z. B. eine beliebig lange Liste von Elementen oder boolesche Bedingungen, möchten Sie den Beitrag möglicherweise als Dokument definieren, das der Client über POST sendet.

Dies ermöglicht eine flexiblere Beschreibung der Suche und vermeidet die Begrenzung der Server-URL-Länge.

estani
quelle
-4

Mein Rat wäre:

/garages
  Returns list of garages (think JSON array here)
/garages/yyy
  Returns specific garage
/garage/yyy/cars
  Returns list of cars in garage
/garages/cars
  Returns list of all cars in all garages (may not be practical of course)
/cars
  Returns list of all cars
/cars/xxx
  Returns specific car
/cars/colors
  Returns lists of all posible colors for cars
/cars/colors/red,blue,green
  Returns list of cars of the specific colors (yes commas are allowed :) )

Bearbeiten:

/cars/colors/red,blue,green/doors/2
  Returns list of all red,blue, and green cars with 2 doors.
/cars/type/hatchback,coupe/colors/red,blue,green/
  Same idea as the above but a lil more intuitive.
/cars/colors/red,blue,green/doors/two-door,four-door
  All cars that are red, blue, green and have either two or four doors.

Hoffentlich kommt Ihnen das auf die Idee. Im Wesentlichen sollte Ihre Rest-API leicht erkennbar sein und Ihnen das Durchsuchen Ihrer Daten ermöglichen. Ein weiterer Vorteil bei der Verwendung von URLs und nicht von Abfragezeichenfolgen besteht darin, dass Sie die auf dem Webserver vorhandenen nativen Caching-Mechanismen für den HTTP-Verkehr nutzen können.

Hier ist ein Link zu einer Seite, die die Übel von Abfragezeichenfolgen in REST beschreibt: http://web.archive.org/web/20070815111413/http://rest.blueoxen.net/cgi-bin/wiki.pl?QueryStringsConsideredHarmful

Ich habe den Google-Cache verwendet, weil die normale Seite für mich nicht funktioniert hat. Hier ist auch dieser Link: http://rest.blueoxen.net/cgi-bin/wiki.pl?QueryStringsConsideredHarmful

Justin Bozonier
quelle
1
Danke für die ausführliche Antwort. Was ist beim letzten Mal, wenn ich nach Farbe und Anzahl der Türen suchen möchte? / Autos / Farben / Rot, Blau, Grün / Türen / 4 Das scheint nicht richtig zu sein.
Parand
2
Kommas in der URL fühlen sich für mich nicht richtig an, aber immer noch gültiger Rest. Ich denke, es ist nur ein Paradigmenwechsel.
Justin Bozonier
21
Ich mag diesen Vorschlag nicht. Wie würden Sie den Unterschied zwischen /cars/colors/red,blue,greenund kennen /cars/colors/green,blue,red? Das Pfadelement der URI sollte hierarchisch sein, und ich sehe das hier nicht wirklich. Ich denke, dies ist eine Situation, in der die Abfragezeichenfolge die am besten geeignete Wahl ist.
troelskn
62
Dies ist eine schlechte Antwort. Der richtige Weg, die Suche zu implementieren, sind Abfragezeichenfolgen. Abfragezeichenfolgen sind bei richtiger Verwendung nicht im geringsten böse. Der zitierte Artikel bezieht sich nicht auf die Suche. Die Beispiele sind eindeutig gefoltert und würden mit mehr Parametern nicht gut zurechtkommen.
pbreitenbach
4
Abfrageringe wurden hauptsächlich erstellt, um das Problem der Abfrage einer Ressource auch mit mehreren Parametern zu lösen. Das Pervertieren des URI, um eine "RESTful" -API zu aktivieren, erscheint gefährlich und kurzsichtig - insbesondere, da Sie Ihre eigenen komplexen Zuordnungen schreiben müssten, um die verschiedenen Permutationen von Parametern auf dem URI zu verarbeiten. Besser noch, verwenden Sie den bereits vorhandenen Begriff der Verwendung von Semikolons in Ihren URIs: doriantaylor.com/policy/http-url-path-parameter-syntax
Anatoly G