it-swarm.com.de

Wie gestalte ich RESTful Search / Filtering?

Ich entwerfe und implementiere derzeit eine RESTful-API in PHP. Ich konnte mein ursprüngliches Design jedoch nicht implementieren.

GET /users # list of users
GET /user/1 # get user with id 1
POST /user # create new user
PUT /user/1 # modify user with id 1
DELETE /user/1 # delete user with id 1

Soweit ziemlich normal, oder?

Mein Problem ist mit dem ersten GET /users. Ich habe überlegt, Parameter im Anforderungshauptteil zu senden, um die Liste zu filtern. Dies liegt daran, dass ich komplexe Filter angeben möchte, ohne eine sehr lange URL zu erhalten, wie zum Beispiel:

GET /users?parameter1=value1&parameter2=value2&parameter3=value3&parameter4=value4

Stattdessen wollte ich etwas haben wie:

GET /users
# Request body:
{
    "parameter1": "value1",
    "parameter2": "value2",
    "parameter3": "value3",
    "parameter4": "value4"
}

das ist viel besser lesbar und bietet Ihnen großartige Möglichkeiten, komplexe Filter zu setzen.

Auf jeden Fall hat file_get_contents('php://input') den Anforderungshauptteil für GET Anforderungen nicht zurückgegeben. Ich habe auch http_get_request_body() ausprobiert, aber das gemeinsam genutzte Hosting, das ich verwende, verfügt nicht über pecl_http. Ich bin mir nicht sicher, ob es trotzdem geholfen hätte.

Ich fand diese Frage und erkannte, dass GET wahrscheinlich keinen Anfragetext haben sollte. Es war ein bisschen nicht schlüssig, aber sie rieten davon ab.

Jetzt bin ich mir nicht sicher, was ich tun soll. Wie gestaltet man eine RESTful-Such-/Filterfunktion?

Ich nehme an, ich könnte POST verwenden, aber das scheint nicht sehr RUHIG zu sein.

406
Erik B

Der beste Weg, eine REST-Suche zu implementieren, ist, die Suche selbst als Ressource zu betrachten. Dann können Sie das Verb POST verwenden, da Sie eine Suche erstellen. Sie müssen nicht buchstäblich etwas in einer Datenbank erstellen, um einen POST zu verwenden.

Zum Beispiel:

Accept: application/json
Content-Type: application/json
POST http://example.com/people/searches
{
  "terms": {
    "ssn": "123456789"
  },
  "order": { ... },
  ...
}

Sie erstellen eine Suche vom Standpunkt des Benutzers aus. Die Implementierungsdetails hierfür sind irrelevant. Einige RESTful-APIs benötigen möglicherweise nicht einmal eine Persistenz. Das ist ein Implementierungsdetail.

379
Jason Harrelson

Wenn Sie den Anforderungshauptteil in einer GET-Anforderung verwenden, brechen Sie das REST -Prinzip, da Ihre GET-Anforderung nicht zwischengespeichert werden kann, da das Cache-System nur die URL verwendet.

Und was noch schlimmer ist, Ihre URL kann nicht mit einem Lesezeichen versehen werden, da die URL nicht alle Informationen enthält, die erforderlich sind, um den Benutzer auf diese Seite umzuleiten

Verwenden Sie URL- oder Abfrageparameter anstelle von Anforderungshauptteilparametern.

z.B.:

/myapp?var1=xxxx&var2=xxxx
/myapp;var1=xxxx/resource;var2=xxxx 

Tatsächlich besagt der HTTP-RFC 7231 Folgendes:

Eine Nutzlast in einer GET-Anforderungsnachricht hat keine definierte Semantik. Das Senden eines Nutzlastkörpers für eine GET-Anforderung kann dazu führen, dass einige vorhandene Implementierungen die Anforderung ablehnen.

Weitere Informationen finden Sie unter hier

130
jfcorugedo

Es scheint, dass das Filtern/Suchen von Ressourcen auf eine REST-gerechte Weise implementiert werden kann. Die Idee ist, einen neuen Endpunkt namens /filters/ oder /api/filters/ einzuführen.

Mit diesem Endpunkt kann filter als Ressource betrachtet und somit über die Methode POST erstellt werden. Auf diese Weise können natürlich alle Parameter in body gespeichert und komplexe Such-/Filterstrukturen erstellt werden.

Nach dem Erstellen eines solchen Filters gibt es zwei Möglichkeiten, das Such-/Filterergebnis zu erhalten.

  1. Eine neue Ressource mit einer eindeutigen ID wird zusammen mit dem Statuscode 201 Created zurückgegeben. Mit dieser ID kann dann eine GET Anfrage an /api/users/ gestellt werden:

    GET /api/users/?filterId=1234-abcd
    
  2. Nachdem ein neuer Filter über POST erstellt wurde, antwortet er nicht mit 201 Created, sondern sofort mit 303 SeeOther zusammen mit dem Location -Header, der auf /api/users/?filterId=1234-abcd zeigt. Diese Weiterleitung wird automatisch über die zugrunde liegende Bibliothek abgewickelt.

In beiden Szenarien müssen zwei Anforderungen gestellt werden, um die gefilterten Ergebnisse zu erhalten - dies kann als Nachteil angesehen werden, insbesondere für mobile Anwendungen. Für mobile Anwendungen würde ich einen einzelnen POST Aufruf von /api/users/filter/ verwenden.

Wie werden erstellte Filter beibehalten?

Sie können im DB gespeichert und später verwendet werden. Sie können auch in einem temporären Speicher aufbewahrt werden, z. redis und haben einige TTL, nach denen sie ablaufen und entfernt werden.

Was sind die Vorteile dieser Idee?

Filter, gefilterte Ergebnisse sind zwischenspeicherbar und können sogar mit Lesezeichen versehen werden.

62
Opal

Ich denke, Sie sollten mit Anforderungsparametern gehen, aber nur, solange es keinen geeigneten HTTP-Header gibt, um zu erreichen, was Sie tun möchten. Die HTTP-Spezifikation besagt nicht explizit, dass GET keinen Body haben kann. Jedoch dieses Papier besagt:

Gemäß der Konvention werden bei Verwendung der GET-Methode alle zur Identifizierung der Ressource erforderlichen Informationen in der URI codiert. In HTTP/1.1 gibt es keine Konvention für eine sichere Interaktion (z. B. Abrufen), bei der der Client Daten an den Server in einem HTTP-Entitätskörper statt im Abfrageteil eines URI liefert. Dies bedeutet, dass URIs für einen sicheren Betrieb möglicherweise lang sind.

15
Daff

Ärgern Sie sich nicht zu sehr, wenn Ihre ursprüngliche API vollständig RESTful ist oder nicht (insbesondere, wenn Sie sich gerade in der Alpha-Phase befinden). Bringen Sie die Back-End-Installation zuerst zum Laufen. Sie können immer eine Art von URL-Transformation/-Rewriting durchführen, um die Dinge abzubilden und iterativ zu verfeinern, bis Sie etwas erhalten, das stabil genug für umfassende Tests ist ("Beta").

Sie können URIs definieren, deren Parameter nach Position und Konvention in den URIs selbst codiert sind, wobei ein Pfad vorangestellt wird, von dem Sie wissen, dass Sie immer eine Zuordnung zu etwas vornehmen. Ich kenne PHP nicht, aber ich würde annehmen, dass eine solche Funktion existiert (wie es in anderen Sprachen mit Web-Frameworks existiert):

.ie. Führen Sie eine "Benutzer" -Suche mit param [i] = value [i] for i = 1..4 in Speicher 1 durch (mit value1, value2, value3, ... als Kurzform für URI-Abfrageparameter):

1) GET /store1/search/user/value1,value2,value3,value4

oder

2) GET /store1/search/user,value1,value2,value3,value4

oder wie folgt (obwohl ich es nicht empfehlen würde, dazu später mehr)

3) GET /search/store1,user,value1,value2,value3,value4

Mit Option 1 ordnen Sie alle URIs mit dem Präfix /store1/search/user dem Suchhandler (oder der Bezeichnung PHP) zu, mit der standardmäßig nach Ressourcen unter store1 gesucht wird (entspricht /search?location=store1&type=user).

Gemäß Konvention, die von der API dokumentiert und erzwungen wird, werden die Parameterwerte 1 bis 4 durch Kommas getrennt und in dieser Reihenfolge dargestellt.

Option 2 fügt den Suchtyp (in diesem Fall user) als Positionsparameter # 1 hinzu. Jede Option ist nur eine kosmetische Wahl.

Option 3 ist ebenfalls möglich, aber ich glaube nicht, dass es mir gefallen würde. Ich denke, die Möglichkeit der Suche in bestimmten Ressourcen sollte in der URI selbst vor der Suche selbst dargestellt werden (als würde in der URI deutlich angegeben, dass die Suche innerhalb der Ressource spezifisch ist.)

Der Vorteil gegenüber der Übergabe von Parametern an den URI besteht darin, dass die Suche Teil des URI ist (und somit eine Suche als Ressource behandelt, eine Ressource, deren Inhalt sich im Laufe der Zeit ändern kann und wird). Der Nachteil besteht darin, dass die Parameterreihenfolge obligatorisch ist .

Sobald Sie so etwas getan haben, können Sie GET verwenden und es wäre eine schreibgeschützte Ressource (da Sie nicht POST oder PUT können - es wird aktualisiert, wenn es GET'ed ist). Es wäre auch eine Ressource, die nur dann existiert, wenn sie aufgerufen wird.

Sie können dem Cache auch eine weitere Semantik hinzufügen, indem Sie die Ergebnisse für einen bestimmten Zeitraum zwischenspeichern oder mit DELETE den Cache löschen. Dies könnte jedoch dem widersprechen, wofür normalerweise DELETE verwendet wird (und weil normalerweise das Caching mit Caching-Headern gesteuert wird).

Wie Sie vorgehen, wäre eine Designentscheidung, aber so würde ich vorgehen. Es ist nicht perfekt und ich bin sicher, dass es Fälle geben wird, in denen dies nicht das Beste ist (insbesondere für sehr komplexe Suchkriterien).

8
luis.espinal

Da ich ein laravel/php Backend verwende, tendiere ich dazu, folgendes zu tun:

/resource?filters[status_id]=1&filters[city]=Sydney&page=2&include=relatedResource

PHP wandelt [] -Params automatisch in ein Array um. In diesem Beispiel werde ich also eine $filter -Variable erhalten, die ein Array/Objekt von Filtern zusammen mit einer Seite und den zugehörigen Ressourcen enthält, die ich unbedingt haben möchte geladen.

Wenn Sie eine andere Sprache verwenden, ist dies möglicherweise immer noch eine gute Konvention, und Sie können einen Parser erstellen, um [] in ein Array zu konvertieren.

8
the-a-train

Zu Ihrer Information: Ich weiß, das ist ein bisschen spät, aber für alle, die interessiert sind. Abhängig davon, wie restvoll Sie sein möchten, müssen Sie Ihre eigenen Filterstrategien implementieren, da die HTTP-Spezifikation diesbezüglich nicht sehr klar ist. Ich möchte vorschlagen, dass alle Filterparameter url-codiert werden, z.

GET api/users?filter=param1%3Dvalue1%26param2%3Dvalue2

Ich weiß, dass es hässlich ist, aber ich denke, es ist der RESTVOLLSTE Weg, es zu tun, und es sollte einfach sein, es auf der Serverseite zu analysieren :)

2
shanks