RESTful API. Soll ich das erstellte / aktualisierte Objekt zurückgeben?

36

Ich entwerfe einen RESTful-Webdienst mit WebApi und frage mich, welche HTTP-Antworten und -Antwortkörper beim Aktualisieren / Erstellen von Objekten zurückgegeben werden sollen.

Zum Beispiel kann ich die POST-Methode verwenden, um JSON an den Webdienst zu senden und dann ein Objekt zu erstellen. Ist es empfehlenswert, den HTTP-Status auf "Erstellt" (201) oder "OK" (200) zu setzen und einfach eine Nachricht wie "Neuer Mitarbeiter hinzugefügt" oder das ursprünglich gesendete Objekt zurückzugeben?

Gleiches gilt für die PUT-Methode. Welcher HTTP-Status ist am besten zu verwenden und muss ich das erstellte Objekt oder nur eine Nachricht zurückgeben? Berücksichtigt man die Tatsache, dass der Benutzer weiß, welches Objekt er ohnehin erstellen / aktualisieren möchte.

Irgendwelche Gedanken?

Beispiel:

Neuen Mitarbeiter hinzufügen:

POST /api/employee HTTP/1.1
Host: localhost:8000
Content-Type: application/json

{
    "Employee": {
        "Name" : "Joe Bloggs",
        "Department" : "Finance"
    }
}

Bestehenden Mitarbeiter aktualisieren:

PUT /api/employee HTTP/1.1
Host: localhost:8000
Content-Type: application/json

{
    "Employee": {
        "Id" : 1
        "Name" : "Joe Bloggs",
        "Department" : "IT"
    }
}

Antworten:

Antwort mit Objekt erstellt / aktualisiert

HTTP/1.1 201 Created
Content-Length: 39
Content-Type: application/json; charset=utf-8
Date: Mon, 28 Mar 2016 14:32:39 GMT

{
    "Employee": {
        "Id" : 1
        "Name" : "Joe Bloggs",
        "Department" : "IT"
    }
}

Antwort mit nur einer Nachricht:

HTTP/1.1 200 OK
Content-Length: 39
Content-Type: application/json; charset=utf-8
Date: Mon, 28 Mar 2016 14:32:39 GMT

{
    "Message": "Employee updated"
}

Antwort nur mit Statuscode:

HTTP/1.1 204 No Content
Content-Length: 39
Date: Mon, 28 Mar 2016 14:32:39 GMT
iswinky
quelle
2
Gute Frage, aber die Verwendung des Begriffs "Best Practice" ist auf dieser Website tabu meta.programmers.stackexchange.com/questions/2442/… Vielleicht möchten Sie die Frage nur umformulieren . meta.programmers.stackexchange.com/questions/6967/…
Snoop
3
Wäre es im Nachhinein eine gute Idee, eine Markierung in der Anforderung zu haben, damit (zum Beispiel) eine mobile Anwendung das gesamte Objekt im WLAN zurückerhalten kann, aber nur die ID, wenn Mobilfunkdaten verwendet werden? Gibt es einen Header, der dafür verwendet werden sollte, um eine Verschmutzung des JSON zu vermeiden?
Andrew sagt Reinstate Monica
@AndrewPiliser Interessante Idee, obwohl ich persönlich der Meinung bin, dass es am besten ist, einen Ansatz zu wählen und dabei zu bleiben. Optimieren Sie dann, wenn sich Ihre Anwendung entwickelt oder populärer wird
iswinky am
@ AndrewPiliser Ihre Idee ist sehr ähnlich der Postgresql- UPDATE/INSERT ... RETURNINGVariante für SQL. Es ist äußerst praktisch, zumal es die Übermittlung neuer Daten und die Anforderung der aktualisierten Atomic-Version aufrechterhält.
Beldaz

Antworten:

31

Wie bei den meisten Dingen kommt es darauf an. Ihr Kompromiss ist die Benutzerfreundlichkeit im Verhältnis zur Netzwerkgröße. Es kann für Kunden sehr hilfreich sein, die erstellte Ressource zu sehen. Es kann Felder enthalten, die vom Server ausgefüllt werden, z. B. zum Zeitpunkt der letzten Erstellung. Da Sie anscheinend das einschließen, idanstatt es zu verwenden hateoas, möchten Clients wahrscheinlich die ID für die Ressource sehen, die sie gerade POSTbearbeitet haben.

Wenn Sie die erstellte Ressource nicht einschließen, erstellen Sie keine beliebige Nachricht. Die Felder 2xx und Location bieten genügend Informationen, damit Kunden sicher sein können, dass ihre Anfrage ordnungsgemäß bearbeitet wurde.

Eric Stein
quelle
+1 Das hateoas Ziel, den Client nicht die URLs erstellen zu lassen, kann auch dadurch erreicht werden, dass der Client vom Server bereitgestellte URL-Vorlagen mit bestimmten IDs ausfüllt. Ja, der Kunde "komponiert", aber nur auf eine Art "Lücken füllen". Es ist zwar kein reines HATEOAS, aber es erreicht das Ziel und macht die Arbeit mit Objekten mit einer (großen) Anzahl von "Aktions" -Uri etwas weniger bandbreitensensitiv, ganz zu schweigen davon, wenn Sie diese Objekte in eine (große) Liste aufnehmen.
Marjan Venema
3
+1 auf den Ratschlag "Bitte keine willkürliche Nachricht erstellen"
HairOfTheDog
Konzentriert sich die Meldung "Keine willkürliche Meldung" auf Zeichenfolgenmeldungen oder auf Rückgabewerte , die nicht die erstellte Ressource sind? Ich konzentriere mich auf Fälle, in denen wir die ID der erstellten Ressource zurückgeben (aber nicht die Ressource selbst) und frage mich, wo dies passt.
Flater
12

Ich persönlich kehre immer nur zurück 200 OK.

Um Ihre Frage zu zitieren

Berücksichtigt man die Tatsache, dass der Benutzer weiß, welches Objekt er ohnehin erstellen / aktualisieren möchte.

Warum zusätzliche Bandbreite hinzufügen (die möglicherweise bezahlt werden muss), um dem Kunden mitzuteilen, was er bereits weiß?

Mawg
quelle
1
Das ist , was ich dachte, wenn es ungültig Sie Validierungsmeldungen zurückgeben kann, aber wenn es gültig ist und wird erstellt / aktualisiert dann den HTTP - Statuscode überprüfen und zeigen dem Benutzer eine Nachricht zB „Hooray“ basiert darauf , dass
iswinky
3
Informationen zu 200/ finden Sie unter stackoverflow.com/a/827045/290182204 No Content , um Verwirrung bei jQuery und dergleichen zu vermeiden.
Beldaz
10

@iswinky Ich würde die Nutzdaten immer zurücksenden, sowohl bei POST als auch bei PUT.

Bei einem POST können Sie die Entität mit einer internen ID oder einer UUID erstellen. Daher ist es sinnvoll, die Nutzlast zurückzusenden.

In ähnlicher Weise könnten Sie im Fall von PUT einige Felder des Benutzers ignorieren (z. B. unveränderliche Werte), oder im Fall eines PATCHs könnten die Daten auch von anderen Benutzern geändert worden sein.

Es ist immer eine gute Idee, die Daten so zurückzusenden, wie sie beibehalten wurden, und sie schaden definitiv nicht. Wenn der Anrufer diese zurückgegebenen Daten nicht benötigt, verarbeitet er sie nicht, sondern verwendet nur den statusCode. Andernfalls können sie diese Daten verwenden, um die Benutzeroberfläche zu aktualisieren.

Nur im Falle eines DELETE würde ich die Nutzdaten nicht zurücksenden und entweder eine 200 mit einem Antwortinhalt oder eine 204 ohne Antwortinhalt ausführen.

Edit: Dank einiger Kommentare von unten schreibe ich meine Antwort um. Ich stehe nach wie vor zu der Art und Weise, wie ich meine APIs entwerfe und Antworten zurücksende, aber ich halte es für sinnvoll, einige meiner Entwurfsüberlegungen zu qualifizieren.

a) Wenn ich sage, dass die Nutzdaten zurückgesendet werden sollen, wollte ich eigentlich sagen, dass die Daten der Ressource zurückgesendet werden sollen, nicht die Nutzdaten, die in der Anforderung enthalten sind. Beispiel: Wenn Sie eine Nutzlast zum Erstellen senden, kann ich im Backend andere Entitäten wie UUID und (möglicherweise) Zeitstempel und sogar einige (Diagramm-) Verbindungen erstellen. Ich würde das alles in der Antwort zurückschicken (nicht nur die Nutzlast der Anfrage wie sie ist - was sinnlos ist).

b) Ich würde keine Antworten zurücksenden, wenn die Nutzlast sehr groß ist. Ich habe dies in den Kommentaren besprochen, aber ich möchte darauf hinweisen, dass ich mein Bestes geben würde, um meine APIs oder Ressourcen so zu gestalten, dass keine sehr großen Nutzlasten erforderlich sind. Ich würde versuchen, die Ressourcen in kleinere und verwaltbare Einheiten aufzuteilen, sodass jede Ressource durch 15-20 JSON-Attribute definiert wird und nicht durch größere.

In dem Fall, dass das Objekt sehr groß ist oder das übergeordnete Objekt aktualisiert wird, würde ich die verschachtelten Strukturen als hrefs zurücksenden.

Unterm Strich würde ich auf jeden Fall versuchen, die Daten zurückzusenden, die für den Verbraucher / die Benutzeroberfläche sinnvoll sind, um sie sofort zu verarbeiten, und zwar mit einer atomaren API-Aktion, anstatt 2-5 weitere APIs abrufen zu müssen, nur um die Benutzeroberfläche zu aktualisieren Erstellung / Aktualisierung der Daten.

Server-zu-Server-APIs denken möglicherweise anders darüber. Ich konzentriere mich auf APIs, die eine Benutzererfahrung fahren.

ksprashu
quelle
Ich kann viele Situationen beobachten, in denen das Zurücksenden der gesamten Nutzlast eine schlechte Idee ist, wenn die Nutzlast groß ist.
Beldaz
2
@beldaz völlig einverstanden. YMMV basiert auf dem Design der REST-API. Ich vermeide im Allgemeinen sehr große Objekte und zerlege sie in eine Reihe von Subressourcen / PUTs. Wenn die Nutzlast sehr groß ist, gibt es bessere Möglichkeiten, dies zu tun, und Sie möchten HATEOAS (wie Marjan oben sagt) ausführen, bei dem Sie den Verweis auf das Objekt anstelle des Objekts selbst zurückgeben.
ksprashu
@ksprashu: "Daher ist es sinnvoll, die Nutzdaten zurückzusenden" - ich halte dies für eine schlechte Idee, da auf diese Weise eine Ressource auf viele Arten erhalten werden kann: über GET als Antwort von POST als Antwort von PUT. Dies bedeutet, dass der Kunde 3 Ressourcen mit möglicherweise unterschiedlicher Struktur erhält . Wo, als ob Sie nur URI (Ort) ohne Text zurückgeben würden, wäre der einzige Weg, eine Ressource zu erhalten, GET. Dies würde sicherstellen, dass der Client immer konsistente Antworten erhält.
Mentallurg
@mentallurg Mir ist klar, dass ich dieses Recht möglicherweise nicht formuliert habe. Vielen Dank für den Hinweis. Ich habe meine Antwort bearbeitet. Lassen Sie mich wissen, ob dies sinnvoller ist.
ksprashu
Solange Sie einen Dienst für Ihre Heimarbeit implementieren, ist dies nicht wirklich wichtig. Mach es wie du willst. Sparen Sie Ihre Zeit.
Mentallurg
9

Wenn Sie auf die Link- RFC-Standards verweisen , sollten Sie den Status 201 (erstellt) zurückgeben, wenn Sie die Anforderungsressource erfolgreich mit "Posten" speichern. In den meisten Anwendungen wird die ID der Ressource vom Server selbst generiert. Daher empfiehlt es sich, die ID der erstellten Ressource zurückzugeben. Die Rücksendung des gesamten Objekts ist der Aufwand für die Post-Anfrage. In der Regel wird der URL-Speicherort der neu erstellten Ressource zurückgegeben.

Sie können beispielsweise auf das folgende Beispiel verweisen, in dem das Mitarbeiterobjekt in der Datenbank gespeichert und die URL des neu erstellten Ressourcenobjekts als Antwort zurückgegeben wird.

@RequestMapping(path = "/employees", method = RequestMethod.POST)
public ResponseEntity<Object> saveEmployee(@RequestBody Employee employee) {
        int id = employeeService.saveEmployee(employee);
        URI uri = ServletUriComponentsBuilder.fromCurrentRequest().path("/{id}").buildAndExpand(id).toUri();
        return ResponseEntity.created(uri).build();
}

Dieser Rest-Endpunkt gibt die Antwort wie folgt zurück:

Status 201 - ERSTELLT

Header Location → http: // localhost: 8080 / employee / 1

Shubham Bondarde
quelle
Schön und sauber - werde ab jetzt und darüber hinaus folgen
Hassan Tareq
0

Ich würde eine Nutzlast im Rückgabetext von einem HTTP-Parameter abhängig machen.

In den meisten Fällen ist es am besten, Inhalte an den API-Konsumenten zurückzugeben, um unnötige Roundtrips zu vermeiden (einer der Gründe, warum GraphQL existiert).

In der Tat versuche ich, diese Richtlinie zu beachten, wenn unsere Anwendungen datenintensiver und verteilter werden:

Meine Richtlinie :

Immer wenn ein Anwendungsfall ein GET unmittelbar nach einem POST oder PUT erfordert, ist es möglicherweise am besten, einfach etwas in der POST / PUT-Antwort zurückzugeben.

Wie dies geschieht und welche Art von Inhalten aus einem PUT oder POST zurückgegeben werden sollen, ist anwendungsspezifisch. Jetzt wäre es interessant, wenn die Anwendung den Typ des "Inhalts" im Antworttext parametrisieren könnte (möchten wir nur den Ort des neuen Objekts oder einige der Felder oder das gesamte Objekt usw.).

Eine Anwendung kann eine Reihe von Parametern definieren, die ein POST / PUT empfangen kann, um die Art des "Inhalts" zu steuern, der im Antworttext zurückgegeben werden soll. Oder es könnte eine Art GraphQL-Abfrage als Parameter codieren (ich kann dies als nützlich erachten, aber auch als Alptraum für die Wartung.)

So oder so scheint es mir, dass:

  1. Es ist in Ordnung (und höchstwahrscheinlich wünschenswert), etwas in einem POST / PUT-Antworttext zurückzugeben.
  2. Wie dies gemacht wird, ist anwendungsspezifisch und fast unmöglich zu verallgemeinern.
  3. Sie möchten standardmäßig keinen großen "Kontext" zurückgeben (Verkehrslärm, der den gesamten Grund für die Abkehr von POST-follow-by-GETs zunichte macht).

Also, 1) mach es, aber 2) halte es einfach.

Eine andere Option, die ich gesehen habe, ist das Erstellen alternativer Endpunkte (z. B. / customers für POST / PUT, die nichts im Hauptteil und / customer_with_details für POST / PUT an / customers zurückgeben, aber etwas im Antworthauptteil).

Ich würde diesen Ansatz jedoch vermeiden. Was passiert, wenn Sie berechtigterweise verschiedene Arten von Inhalten zurückgeben müssen? Ein Endpunkt pro Inhaltstyp? Nicht skalierbar oder wartbar.

luis.espinal
quelle