Entwerfen Sie eine RESTful-Abfrage-API mit einer langen Liste von Abfrageparametern [geschlossen].

153

Ich muss eine RESTful-Abfrage-API entwerfen, die eine Reihe von Objekten basierend auf einigen Filtern zurückgibt. Die übliche HTTP-Methode hierfür ist GET. Das einzige Problem ist, dass es mindestens ein Dutzend Filter haben kann. Wenn wir alle als Abfrageparameter übergeben, kann die URL ziemlich lang werden (lang genug, um von einer Firewall blockiert zu werden).

Das Reduzieren der Anzahl von Parametern ist keine Option.

Eine Alternative, die ich mir vorstellen könnte, besteht darin, die POST-Methode für den URI zu verwenden und die Filter als Teil des POST-Körpers zu senden. Ist dies dagegen, RESTfull zu sein (POST-Aufruf zum Abfragen von Daten).

Hat jemand bessere Designvorschläge?

missionE46
quelle
2
Kurze (1-Zeichen usw.) Parameternamen verwenden?
Madbreaks
2
Es mag nicht wirklich RESTful sein, aber ich denke, man muss praktisch sein, wenn es um GETs und POSTs geht. Wenn Sie so viele Variablen senden müssen und diese nicht reduzieren können, würde ich sie POSTEN. Ich mag es nicht, die URL zu überfüllen, aber das bin nur ich.
Doug Dawson
Vielen Dank. Obwohl diese Frage geschlossen ist, ist es genau die Frage, auf die ich eine Antwort brauchte. Ich bin froh, dass du gefragt hast.
Casey Crookston

Antworten:

141

Denken Sie daran, dass bei einer REST-API alles eine Frage Ihrer Sichtweise ist.

Die beiden Schlüsselkonzepte in einer REST-API sind die Endpunkte und die Ressourcen (Entitäten). Ein Endpunkt gibt entweder Ressourcen über GET zurück oder akzeptiert Ressourcen über POST und PUT usw. (oder eine Kombination der oben genannten).

Es wird davon ausgegangen, dass mit POST die von Ihnen gesendeten Daten möglicherweise zur Erstellung einer neuen Ressource und der zugehörigen Endpunkte führen, die höchstwahrscheinlich nicht unter der POST-URL "leben". Mit anderen Worten, wenn Sie POSTEN, senden Sie Daten zur Bearbeitung an einen Ort. Auf dem POST-Endpunkt befindet sich die Ressource normalerweise nicht.

Zitat aus RFC 2616 (wobei irrelevante Teile weggelassen und relevante Teile hervorgehoben werden):

9.5 POST

Die POST-Methode wird verwendet, um anzufordern, dass der Ursprungsserver die in der Anforderung enthaltene Entität als neuen Untergebenen der Ressource akzeptiert, die durch den Anforderungs-URI in der Anforderungszeile identifiziert wird. POST wurde entwickelt, um eine einheitliche Methode zu ermöglichen, die die folgenden Funktionen abdeckt:

  • ...
  • Bereitstellen eines Datenblocks, z. B. des Ergebnisses des Absendens eines Formulars, für einen Datenverarbeitungsprozess;
  • ...

...

Die von der POST-Methode ausgeführte Aktion führt möglicherweise nicht zu einer Ressource, die durch einen URI identifiziert werden kann . In diesem Fall ist entweder 200 (OK) oder 204 (kein Inhalt) der geeignete Antwortstatus, je nachdem, ob die Antwort eine Entität enthält, die das Ergebnis beschreibt oder nicht .

Wenn eine Ressource auf dem Ursprungsserver erstellt wurde, sollte die Antwort 201 (Erstellt) sein ...

Wir haben uns an Endpunkte und Ressourcen gewöhnt, die "Dinge" oder "Daten" darstellen, sei es ein Benutzer, eine Nachricht, ein Buch - was auch immer die Problemdomäne vorschreibt. Ein Endpunkt kann jedoch auch eine andere Ressource verfügbar machen - beispielsweise Suchergebnisse.

Betrachten Sie das folgende Beispiel:

GET    /books?author=AUTHOR
POST   /books
PUT    /books/ID
DELETE /books/ID

Dies ist ein typischer REST CRUD. Was aber, wenn wir hinzufügen:

POST /books/search

    {
        "keywords": "...",
        "yearRange": {"from": 1945, "to": 2003},
        "genre": "..."
    }

An diesem Endpunkt ist nichts Unruhiges. Es akzeptiert Daten (Entitäten) in Form des Anforderungshauptteils. Diese Daten sind die Suchkriterien - ein DTO wie jedes andere. Dieser Endpunkt erzeugt eine Ressource (Entität) als Antwort auf die Anforderung: Suchergebnisse . Die Suchergebnisressource ist eine temporäre Ressource, die dem Client sofort ohne Weiterleitung und ohne Offenlegung durch eine andere kanonische URL bereitgestellt wird.

Es ist immer noch REST, außer dass die Entitäten keine Bücher sind - die Anforderungsentität sind Buchsuchkriterien und die Antwortentität sind Buchsuchergebnisse.

Amir Abiri
quelle
Könnten Sie einige Klassen vorschlagen, die Konventionen für das DTO benennen?
Kwadz
Persönlich würde ich mit BooksSearchCriteriaDTOund gehen BooksSearchResultsDTO.
Amir Abiri
Was wäre der beste HTTP-Antwortcode für diesen Fall von POST / books / search? 201 gilt noch?
L. Holanda
8
201 ist das Gegenteil - es bedeutet, dass eine Ressource erstellt wurde. Eine Ressource, von der erwartet wird, dass sie irgendwo einen eigenen eindeutigen URI hat. 201 ist geeignet, wenn das POSTfür den C-Teil von CRUD verwendet wird. Ich würde mit einfachen alten 200 gehen, optional vielleicht mit 204 für leere Suchergebnisse.
Amir Abiri
@AmirAbiri vielen Dank.
Mohamed-Mhiri
84

Viele Leute haben die Praxis akzeptiert, dass ein GET mit einer zu langen oder zu komplexen Abfragezeichenfolge (z. B. Abfragezeichenfolgen verarbeiten verschachtelte Daten nicht einfach) stattdessen als POST gesendet werden kann, wobei die komplexen / langen Daten im Hauptteil dargestellt werden der Anfrage.

Suchen Sie in der HTTP-Spezifikation nach der POST-Spezifikation. Es ist unglaublich breit. (Wenn Sie ein Schlachtschiff durch eine Lücke in REST segeln möchten, verwenden Sie POST.)

Sie verlieren einige der Vorteile der GET-Semantik ... wie automatische Wiederholungsversuche, weil GET idempotent ist, aber wenn Sie damit leben können, ist es möglicherweise einfacher, die Verarbeitung wirklich langer oder komplizierter Abfragen mit POST zu akzeptieren.

(lol langer Exkurs ... Ich habe kürzlich festgestellt, dass GET nach der HTTP-Spezifikation einen Dokumententext enthalten kann . In einem Abschnitt heißt es umschrieben: "Jede Anfrage kann einen Dokumententext haben, mit Ausnahme der in diesem Abschnitt aufgeführten" ... und der Abschnitt, auf den es verweist, listet keinen auf. Ich habe einen Thread gesucht und gefunden, in dem die HTTP-Autoren darüber gesprochen haben, und es war beabsichtigt, damit Router und dergleichen nicht zwischen verschiedenen Nachrichten unterscheiden müssen Üben Sie, dass viele Infrastrukturelemente den Körper eines GET fallen lassen. Sie könnten also mit im Körper dargestellten Filtern wie POST mit GET arbeiten, aber Sie würden würfeln.)

rauben
quelle
11
Siehe auch diese Frage für weitere Diskussion über HTTP GET mit Körper.
RickyA
6

Kurz gesagt: Erstellen Sie einen POST, überschreiben Sie jedoch die HTTP-Methode mithilfe des X-HTTP-Method-Override- Headers.

Echte Anfrage

POST / Bücher

Körper der Entität

{"title": "Ipsum", "year": 2017}

Überschriften

X-HTTP-Methodenüberschreibung: GET

Überprüfen Sie auf der Serverseite, ob der Header X-HTTP-Method-Override vorhanden ist, und nehmen Sie seinen Wert als Methode zum Erstellen der Route zum endgültigen Endpunkt im Backend. Nehmen Sie auch den Entitätstext als Abfragezeichenfolge. Aus Backend-Sicht wurde die Anfrage nur zu einem einfachen GET.

Auf diese Weise halten Sie das Design im Einklang mit den REST-Prinzipien.

Bearbeiten: Ich weiß, dass diese Lösung ursprünglich dazu gedacht war, das PATCH-Verbproblem in einigen Browsern und Servern zu lösen, aber sie funktioniert auch mit dem GET-Verb bei einer sehr langen URL, bei der es sich um das in der Frage beschriebene Problem handelt.

Delmo
quelle
2
IETF veraltete HTTP-Header mit X-Präfix: tools.ietf.org/html/rfc6648
jannis
@jannis Der von Ihnen verknüpfte RFC bleibt 1.4. es gibt keine Empfehlung zur bestehenden X-Entfernung und 1.5. Bestehende Spezifikationen werden nicht überschrieben. ... X-wird IMO hier bleiben.
Jan Molnar
-3

Wenn Sie in Java und JAX-RS entwickeln, empfehle ich Ihnen, @QueryParam mit @GET zu verwenden

Ich hatte die gleiche Frage, als ich eine Liste durchgehen musste.

Siehe Beispiel:

import java.util.List;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Response;

@Path("/poc")
public class UserService {

    @GET
    @Path("/test/")
    @Produces(MediaType.APPLICATION_JSON)
    public Response test(@QueryParam("code") final List<Integer> code) {
                Integer int0 = codigo.get(0);
                Integer int1 = codigo.get(1);

        return Response.ok(new JSONObject().put("int01", int0)).build();
    }
}

URI-Muster: „poc / test? Code = 1 & code = 2 & code = 3

@QueryParam konvertiert den Abfrageparameter "orderBy = age & orderBy = name" automatisch in java.util.List.

acacio.martins
quelle
Es wäre besser, wenn Sie Ihr Beispiel erklären. In welcher Programmiersprache ist es geschrieben?
Aleks Andreev
Hallo @AleksAndreev. Danke für deine Meinung. Es wurde besser? tks
acacio.martins
Diese Frage bezieht sich auf das Design des RESTful-Service und nicht auf die Implementierung. Diese Antwort beantwortet die Frage nicht.
Heretic Monkey
@ user1331413 IMHO ja, jetzt ist es besser. Vielen Dank für Ihre Bemühungen. Wie Mike McCaughan jedoch sagte, geht es bei der Frage eher um das REST-Konzept als um die Implementierung
Aleks Andreev,