it-swarm.com.de

Wie passen Suchvorgänge in eine RESTful-Oberfläche?

Beim Entwerfen einer RESTful-Schnittstelle wird die Semantik der Anforderungstypen als entscheidend für den Entwurf angesehen.

  • [~ # ~] get [~ # ~] - Liste auflisten oder Element abrufen
  • [~ # ~] put [~ # ~] - Sammlung oder Element ersetzen
  • [~ # ~] post [~ # ~] - Sammlung oder Element erstellen
  • [~ # ~] delete [~ # ~] - Nun, ähm, lösche Sammlung oder Element

Dies scheint jedoch das Konzept der "Suche" nicht abzudecken.

Z.B. Beim Entwerfen einer Suite von Webdiensten, die eine Jobsuche unterstützen, müssen möglicherweise folgende Anforderungen erfüllt sein:

  • Individuelle Stellenanzeige erhalten
    • [~ # ~] bringt [~ # ~] zu domain/Job/{id}/
  • Stellenanzeige erstellen
    • [~ # ~] poste [~ # ~] an domain/Job/
  • Stellenanzeige aktualisieren
    • [~ # ~] setze [~ # ~] auf domain/Job/
  • Stellenanzeige löschen
    • [~ # ~] lösche [~ # ~] zu domain/Job/

"Get All Jobs" ist auch einfach:

  • [~ # ~] bringt [~ # ~] zu domain/Jobs/

Wie fällt die Jobsuche in diese Struktur?

Sie könnten behaupten, es sei eine Variation der "Listensammlung" und implementieren als:

  • [~ # ~] bringt [~ # ~] zu domain/Jobs/

Suchvorgänge can sind jedoch komplex und es ist durchaus möglich, eine Suche zu erstellen, die einen langen GET-String generiert. Das heißt, unter Bezugnahme auf a SO Frage hier gibt es Probleme bei der Verwendung von GET-Zeichenfolgen, die länger als etwa 2000 Zeichen sind.

Ein Beispiel könnte eine facettierte Suche sein - das Beispiel "Job" wird fortgesetzt.

Ich kann die Suche nach Facetten zulassen - "Technologie", "Berufsbezeichnung", "Disziplin" sowie nach Freitext-Schlüsselwörtern, Alter des Arbeitsplatzes, Standort und Gehalt.

Mit einer fließenden Benutzeroberfläche und einer großen Anzahl von Technologien und Berufsbezeichnungen ist es möglich, dass eine Suche eine große Anzahl von Facettenoptionen umfasst.

Wenn Sie dieses Beispiel an Lebensläufen anstatt an Jobs anpassen, erhalten Sie noch mehr Facetten, und Sie können sich sehr leicht eine Suche mit hundert ausgewählten Facetten oder sogar nur 40 Facetten vorstellen, von denen jede 50 Zeichen lang ist (z. B. Jobtitel, Universitätsnamen, Arbeitgebernamen).

In dieser Situation kann es wünschenswert sein, einen PUT oder POST] zu verschieben, um sicherzustellen, dass die Suchdaten korrekt gesendet werden.

  • [~ # ~] poste [~ # ~] an domain/Jobs/

Aber semantisch ist das eine Anweisung zum Erstellen einer Sammlung.

Sie könnten sagen auch, dass Sie dies als Erstellung einer Suche ausdrücken:

  • [~ # ~] poste [~ # ~] an domain/Jobs/Search/

oder (wie durch Burninggramma unten vorgeschlagen)

  • [~ # ~] poste [~ # ~] an domain/JobSearch/

Semantisch mag es sinnvoll erscheinen, aber Sie erstellen eigentlich nichts, Sie fordern Daten an.

Semantisch ist es also ein [~ # ~] get [~ # ~] , aber [~ # ~] get [~ # ~] unterstützt nicht garantiert das, was Sie benötigen.

Die Frage ist also: Versuchen Sie, dem RESTful-Design so treu wie möglich zu bleiben und gleichzeitig sicherzustellen, dass ich mich an die Grenzen von HTTP halte. Welches Design eignet sich am besten für eine Suche?

153
Rob Baillie

Sie sollten nicht vergessen, dass GET Anfragen einige überlegene Vorteile gegenüber anderen Lösungen haben:

1) GET-Anfragen können aus der URL-Leiste kopiert werden, sie werden von Suchmaschinen verdaut, sie sind "freundlich". Wobei "freundlich" bedeutet, dass normalerweise eine GET-Anfrage nichts ändern sollte in Ihrer Anwendung (idempotent). Dies ist der Standardfall für eine Suche.

2) Alle diese Konzepte sind sehr wichtig nicht nur vom Benutzer und der Suchmaschine, sondern auch vom architektonischen API-Design Standpunkt.

3) Wenn Sie mit POST/PUT eine Problemumgehung erstellen, treten Probleme auf, an die Sie gerade nicht denken. Zum Beispiel im Falle eines Browsers die Schaltfläche Zurück navigieren/Seite aktualisieren/Verlauf. Diese können natürlich gelöst werden, aber das wird eine weitere Problemumgehung sein, dann eine weitere und eine weitere ...

In Anbetracht dessen wäre mein Rat:

a) Sie sollten in der Lage sein, mit clevere Parameterstruktur in Ihr GET with zu passen. Im Extremfall können Sie sogar Taktiken wie diese Google-Suche anwenden, bei denen ich viele Parameter eingestellt habe. Es ist immer noch eine super kurze URL.

b) Erstellen Sie eine andere Entität ​​in Ihrer Anwendung wie JobSearch. Angenommen, Sie haben so viele Optionen, ist es wahrscheinlich, dass Sie diese Suchanfragen ebenfalls speichern und verwalten müssen, sodass Ihre Anwendung nur bereinigt wird. Sie können mit den JobSearch -Objekten als Ganzes arbeiten, dh Sie können es testen/verwenden einfacher.


Persönlich würde ich versuchen, mit all meinen Krallen zu kämpfen, um es zu schaffen a) und wenn alle Hoffnung verloren ist, würde ich mit Tränen in den Augen zur Option zurückkriechen b).

101
p1100i

TL; DR: GET zum Filtern, POST zum Suchen

Ich unterscheide zwischen dem Filtern der Ergebnisse aus der Auflistung einer Sammlung und einer komplexen Suche. Der Lackmustest, den ich verwende, ist im Grunde genommen, wenn ich mehr als Filterung (positiv, negativ oder Bereich) benötige . Ich halte es für eine komplexere Suche, die POST erfordert.

Dies wird tendenziell verstärkt, wenn darüber nachgedacht wird, was zurückgegeben wird. Normalerweise verwende ich GET nur, wenn eine Ressource einen größtenteils vollständigen Lebenszyklus hat (PUT, DELETE, GET, Sammlung GET) . Normalerweise gebe ich in einer Sammlung GET eine Liste von URIs zurück, bei denen es sich um die REST -Ressourcen handelt, aus denen diese Sammlung besteht. In einer komplexen Abfrage kann ich aus mehreren Ressourcen ziehen, um die Antwort zu erstellen (denke an SQL Join) , damit ich keine URIs zurücksende, sondern tatsächliche Daten. Das Problem ist, dass die Daten nicht in einer Ressource dargestellt werden. Daher muss ich immer Daten zurückgeben. Dies scheint mir ein klarer Fall zu sein, in dem ein POST erforderlich ist.

- -

Es ist schon eine Weile her und mein ursprünglicher Beitrag war etwas schlampig, also dachte ich, ich würde ihn aktualisieren.

GET ist die intuitive Wahl für die Rückgabe der meisten Arten von Daten, Sammlungen von REST Ressourcen, strukturierte Daten einer Ressource, sogar einzelne Nutzdaten (Bilder, Dokumente usw.).

POST ist die Catchall-Methode für alles, was nicht unter GET, PUT, DELETE usw. zu passen scheint.

An dieser Stelle denke ich, dass einfache Suchvorgänge und Filterung durch GET intuitiv sinnvoll sind. Komplexe Suchvorgänge hängen von Ihren persönlichen Vorlieben ab, insbesondere wenn Sie Aggregationsfunktionen, Kreuzkorrelationen (Joins), Neuformatierer usw. verwenden. Ich würde argumentieren, dass die GET-Parameter nicht zu lang werden sollten und es sich um eine größere Abfrage handelt (wie auch immer sie strukturiert ist) ) kann als POST Anforderungshauptteil) oft sinnvoller sein.

Ich betrachte auch den Erfahrungsaspekt der API-Nutzung. Im Allgemeinen möchte ich die meisten Methoden so einfach und intuitiv wie möglich gestalten. Ich werde Aufrufe, die flexibler (und daher komplexer) sind, in POSTs und auf einen anderen Ressourcen-URI übertragen, insbesondere wenn dies nicht mit dem Verhalten anderer REST - Ressourcen in derselben API übereinstimmt).

In beiden Fällen ist die Konsistenz wahrscheinlich wichtiger als die Suche in GET oder POST.

Hoffe das hilft.

13
dietbuddha

In REST ist die Ressourcendefinition sehr breit. Es ist wirklich jedoch so, dass Sie einige Daten bündeln möchten.

  • Es ist nützlich, sich eine Suchressource als Sammlungsressource vorzustellen. Die Abfrageparameter, die manchmal als durchsuchbarer Teil des URI bezeichnet werden, beschränken die Ressource auf die Elemente, an denen der Client interessiert ist.

Der Haupt-URI von Google verweist beispielsweise auf eine Sammlungsressource mit "Links zu jeder Website im Internet". Abfrageparameter beschränken sich auf die Sites, die Sie sehen möchten.

(URI = Universal Resource Identifier, davon URL = Universal Resource Locator, wobei das bekannte "http: //" das Standardformat für einen URI ist. URL ist also ein Locator, jedoch in REST Es ist gut, dies auf eine Ressourcen-ID zu verallgemeinern. Die Leute verwenden sie jedoch austauschbar.)

  • Da die Ressource, nach der Sie in Ihrem Beispiel suchen, die Jobsammlung ist, ist es sinnvoll, mit zu suchen

Site/Jobs abrufen? Type = blah & location = here & etc = etc.

(return) {jobs: [{job: ...}]}

Verwenden Sie dann POST, das Anhänge- oder Prozessverb, um dieser Sammlung neue Elemente hinzuzufügen:

POST-Site/Jobs

{Job: ...}

  • Beachten Sie, dass es für das Objekt job jeweils dieselbe Struktur gibt. Ein Client kann eine Sammlung von Jobs abrufen, indem er Abfrageparameter verwendet, um die Suche einzugrenzen, und dann dasselbe Format für eines der Elemente verwenden, um POST einen neuen Job). Oder er kann einen dieser Jobs annehmen Elemente und PUT auf seinen URI, um diesen zu aktualisieren.

  • Bei wirklich langen oder komplizierten Abfragezeichenfolgen ist es gemäß der Konvention in Ordnung, diese als POST - Anforderungen zu senden. Bündeln Sie die Abfrageparameter als Name/Wert-Paare oder verschachtelte Objekte in einer JSON- oder XML-Struktur und Senden Sie es im Hauptteil der Anforderung. Wenn Ihre Abfrage beispielsweise verschachtelte Daten anstelle einer Reihe von Name/Wert-Paaren enthält. Die HTTP-Spezifikation für POST beschreibt sie als Anhänge- oder Prozessverb (Wenn Sie ein Schlachtschiff durch eine Lücke in REST segeln möchten, verwenden Sie POST.)

Ich würde das jedoch als Fallback-Plan verwenden.

Was Sie dabei verlieren, ist a) GET ist nullipotent - das heißt, es ändert nichts - POST ist nicht. Wenn der Aufruf fehlschlägt, wird Middleware dies nicht tun Ergebnisse automatisch wiederholen oder zwischenspeichern und 2) mit den Suchparametern im Textkörper können Sie den URI nicht mehr ausschneiden und einfügen. Das heißt, der URI ist keine spezifische Kennung für die gewünschte Suche.

Um zwischen "erstellen" und "suchen" zu unterscheiden. Es gibt einige Optionen, die mit REST Praxis:

  • Sie können dies in der URI tun, indem Sie dem Namen der Sammlung etwas hinzufügen, z. B. Jobsuche anstelle von Jobs. Das bedeutet nur, dass Sie die Suchsammlung als separate Ressource behandeln.

  • Da die Semantik von POST beide append OR) ist, können Sie Suchkörper mit der Nutzlast identifizieren. Wie {job: ...} vs. {search : ...}. Es liegt an der Logik POST), sie entsprechend zu veröffentlichen oder zu verarbeiten.

Das ist so ziemlich eine Design-/Implementierungspräferenz. Ich glaube nicht, dass es eine klare Konvention gibt.

Wie Sie bereits dargelegt haben, besteht die Idee darin, eine Sammlungsressource für jobs zu definieren

website/Jobs

Suchen Sie mit GET + Abfrageparametern, um die Suche einzugrenzen. Lange oder strukturierte Datenabfragen werden in den Hauptteil einer POST (möglicherweise zu einer separaten Suchsammlung)) verschoben. Erstellen Sie mit POST, um sie an die Sammlung anzuhängen. Und aktualisieren Sie sie mit PUT zu einem bestimmten URI.

(FWIW Die Stilkonvention mit URIs besteht darin, alle Kleinbuchstaben mit durch Bindestriche getrennten Wörtern zu verwenden. Das bedeutet jedoch nicht, dass Sie dies so tun müssen.)

(Außerdem sollte ich sagen, dass aus Ihrer Frage hervorgeht, dass Sie auf diesem Weg weit sind. Ich habe die Dinge explizit formuliert, nur um sie in eine Reihe zu bringen, aber Ihre Frage hatte bereits die meisten semantischen Probleme in dieser Frage angesprochen antworte. Ich habe es nur mit etwas Konvention und Übung geschnürt.)

10
Rob

Ich verwende im Allgemeinen OData-Abfragen, sie funktionieren als GET-Aufruf, aber Sie können die zurückgegebenen Eigenschaften einschränken und filtern.

Sie verwenden Token wie $select= Und $filter=, So dass Sie eine URI erhalten, die ungefähr so ​​aussieht:

/users?$select=Id,Name$filter=endswith(Name, 'Smith')

Sie können auch mit $skip Und $top Pagen und bestellen.

Weitere Informationen finden Sie unter OData.org . Sie haben nicht angegeben, welche Sprache Sie verwenden, aber wenn es sich um ASP.NET handelt, unterstützt die WebApi-Plattform OData-Abfragen. Für andere (PHP usw.) gibt es wahrscheinlich Bibliotheken, mit denen Sie sie in Datenbankabfragen übersetzen können.

8
Trevor Pilley

Dies ist eine alte Antwort, aber ich kann noch ein wenig zur Diskussion beitragen. Ich habe sehr oft ein Missverständnis von REST, RESTful und Architektur beobachtet. RESTful erwähnt nie etwas darüber, KEINE Suche zu erstellen, es gibt nichts in RESTful über Architektur, es ist eine Reihe von Designprinzipien oder -kriterien.

Um eine Suche besser zu beschreiben, müssen wir insbesondere über eine Architektur sprechen, und diejenige, die besser passt, ist die ressourcenorientierte Architektur (ROA).

In RESTful gibt es Prinzipien zu entwerfen. Idempotent bedeutet nicht, dass sich das Ergebnis nicht ändern kann, wenn ich einige Antworten einlese. Es bedeutet, dass das Ergebnis einer unabhängigen Anforderung nicht davon abhängt, wie oft ausgeführt wird. Es kann sich ändern. Stellen wir uns vor, ich aktualisiere ständig eine Datenbank, die sie mit einigen Daten versorgt, die von einer RESTful-API bereitgestellt werden. Wenn Sie dasselbe GET ausführen, ändert sich möglicherweise das Ergebnis, aber es hängt nicht davon ab, wie oft es ausgeführt wurde. Wenn ich in der Lage bin, die Welt einzufrieren, bedeutet dies, dass es keinen Status, keine Transformation oder irgendetwas innerhalb des Dienstes gibt, wenn ich die Ressource anfordere, die zu einem anderen Ergebnis führt.

Per Definition ist eine Ressource alles, was wichtig ist, um als eigenständige Sache bezeichnet zu werden.

In einer ressourcenorientierten Architektur (der Kürze halber aus Gründen der Kürze als ROA bezeichnet) konzentrieren wir uns auf die Ressource, die viele Dinge sein kann:

  • Eine Version eines Dokuments
  • Die letzte aktualisierte Version des Dokuments
  • Ein Ergebnis einer Suche
  • Eine Liste von Objekten
  • Der erste Artikel, den ich bei einem E-Commerce gekauft habe

Was es in Bezug auf die Ressource einzigartig macht, ist die Adressierbarkeit, was bedeutet, dass es nur eine URI hat

Auf diese Weise passt die Suche perfekt in RESTful unter Berücksichtigung von ROA. Wir müssen GET verwenden, da ich davon ausgehe, dass Ihre Suche eine normale Suche ist und nichts ändert, sodass sie idempotent ist (auch wenn sie abhängig von den hinzugefügten neuen Elementen unterschiedliche Dinge zurückgibt). Auf diese Weise gibt es hier eine Verwirrung, weil ich mich an RESTful und nicht an ROA halten könnte. Dies bedeutet, dass ich einem Muster folgen könnte, das eine Suche erstellt und verschiedene Dinge mit denselben Parametern zurückgibt, weil ich nicht das Adressierbarkeitsprinzip von ROA verwende. Wie ist das? Wenn Sie die Suchfilter im Body oder Header senden, ist die Ressource nicht ADRESSIERBAR.

Die Prinzipien von genau und URI finden Sie im W3-Originaldokument:

https://www.w3.org/DesignIssues/Axioms

Jede URL in dieser Architektur muss selbstbeschreibend sein. Wenn Sie die Prinzipien befolgen, um alles in der URI zu adressieren, bedeutet dies, dass Sie/(Schrägstrich) verwenden können, um alles zu trennen, was Sie benötigen, oder Parameter abzufragen. Wir wissen, dass dies Einschränkungen unterliegt, aber dies ist das Architekturmuster.

Nach dem ROA-Muster in RESTful ist eine Suche nicht mehr als jede andere Ressource. Der einzige Unterschied besteht darin, dass die Ressourcen aus einer Berechnung stammen und nicht aus einer direkten Beziehung zum Objekt selbst. Basierend auf dem Prinzip konnte ich einen einfachen arithmetischen Berechnungsdienst basierend auf dem folgenden Muster ansprechen und erhalten:

http://myapi.com/sum/1/2

Wenn Summe, 1 und 2 geändert werden können, das Ergebnis der Berechnung jedoch eindeutig und adressierbar ist, erhalte ich jedes Mal, wenn ich mit denselben Parametern aufrufe, dieselben und nichts ändert sich im Dienst. Die Ressourcen/sum/1/2 und/subtrahieren/5/4 halten sich perfekt an die Prinzipien.

7

Ein zu berücksichtigender Ansatz besteht darin, den Satz möglicher Abfragen als Sammlungsressource zu behandeln, z. /jobs/filters.

POST Anforderungen an diese Ressource mit den Abfrageparametern im Hauptteil erstellen entweder eine neue Ressource oder identifizieren einen vorhandenen äquivalenten Filter und geben eine URL zurück, die ihre ID enthält: /jobs/filters/12345.

Die ID kann dann in einer GET-Anforderung für Jobs verwendet werden: /jobs?filter=12345. Nachfolgende GET -Anforderungen an die Filterressource geben die Definition des Filters zurück.

Dieser Ansatz hat den Vorteil, dass Sie vom Abfrageparameterformat für die Filterdefinition befreit werden und möglicherweise mehr Möglichkeiten zum Definieren komplexer Filter erhalten. OR Bedingungen sind ein Beispiel, von dem ich mir vorstellen kann, dass es mit Abfragezeichenfolgen schwierig ist, dies zu erreichen.

Ein Nachteil dieses Ansatzes besteht darin, dass Sie die Lesbarkeit der URL verlieren (obwohl dies durch Abrufen der Definition durch eine GET -Anforderung für die Filterressource gemildert werden kann). Aus diesem Grund möchten Sie möglicherweise auch dieselbe oder eine Teilmenge der Abfrageparameter für die Ressource /jobs Unterstützen, die Sie für eine Filterressource unterstützen würden. Dies kann für kürzere Abfragen verwendet werden. Wenn diese Funktion bereitgestellt wird, sollte die Implementierung bei Verwendung von Abfrageparametern für die Ressource /jobs Intern eine Filterressource erstellen/wiederverwenden und einen 302 oder 303 Status, der die URL in Form von /jobs?filter=12345 angibt.

5
pgraham

GET ist in Ordnung, wenn Sie eine statische Sammlung haben, die immer die gleichen Ergebnisse (Darstellung) für einen URI zurückgibt. Dies bedeutet auch, dass die Daten, die diese Darstellungen erzeugen, niemals geändert werden. Die Quelle ist eine schreibgeschützte Datenbank.

Wenn GET unterschiedliche Ergebnisse für ein und denselben URI zurückgibt, verstößt dies gegen Idempotenz/Sicherheit und das CoolURI-Prinzip und ist folglich nicht RESTful . Es ist möglich, dass idempotente Verben in eine Datenbank geschrieben werden, sie dürfen jedoch niemals die Darstellung beeinflussen.

Eine allgemeine Suche beginnt mit einer POST -Anforderung, die einen Verweis auf das Ergebnis zurückgibt. Sie generiert das Ergebnis (es ist neu und kann mit einem nachfolgenden GET abgerufen werden). Dieses Ergebnis kann hierarchisch sein (weiter) Verweise auf URIs, die Sie abrufen können) und möglicherweise Elemente früherer Suchvorgänge wiederverwenden, wenn dies für die Anwendung sinnvoll ist.

Übrigens weiß ich, dass die Leute es anders machen. Sie müssen mir nicht erklären, wie bequem es sein kann, REST zu verletzen.

3