HTTP PUT vs HTTP PATCH in einer REST-API

1. Übersicht

In diesem kurzen Artikel untersuchen wir die Unterschiede zwischen den Verben HTTP PUT und PATCH sowie die Semantik der beiden Operationen.

Wir werden Spring verwenden, um zwei REST-Endpunkte zu implementieren, die diese beiden Arten von Vorgängen unterstützen, und um die Unterschiede und die richtige Verwendung besser zu verstehen.

2. Wann Put und Wann Patch verwenden?

Beginnen wir mit einer einfachen und leicht einfachen Aussage.

Wenn ein Client eine vorhandene Ressource vollständig ersetzen muss, kann er PUT verwenden. Wenn sie eine teilweise Aktualisierung durchführen, können sie HTTP PATCH verwenden.

Wenn Sie beispielsweise ein einzelnes Feld der Ressource aktualisieren, kann das Senden der vollständigen Ressourcendarstellung umständlich sein und viel unnötige Bandbreite beanspruchen. In solchen Fällen ist die Semantik von PATCH viel sinnvoller.

Ein weiterer wichtiger Aspekt, der hier berücksichtigt werden muss, ist die Idempotenz. PUT ist idempotent; PATCH kann sein, muss es aber nicht . Abhängig von der Semantik der Operation, die wir implementieren, können wir basierend auf dieser Eigenschaft auch die eine oder andere auswählen.

3. Implementierung von PUT und PATCH Logic

Angenommen , wir möchten die REST-API zum Aktualisieren einer HeavyResource mit mehreren Feldern implementieren :

public class HeavyResource { private Integer id; private String name; private String address; // ...

Zuerst müssen wir den Endpunkt erstellen, der eine vollständige Aktualisierung der Ressource mithilfe von PUT verarbeitet:

@PutMapping("/heavyresource/{id}") public ResponseEntity saveResource(@RequestBody HeavyResource heavyResource, @PathVariable("id") String id) { heavyResourceRepository.save(heavyResource, id); return ResponseEntity.ok("resource saved"); }

Dies ist ein Standardendpunkt zum Aktualisieren von Ressourcen.

Angenommen, das Adressfeld wird häufig vom Client aktualisiert. In diesem Fall möchten wir nicht das gesamte HeavyResource- Objekt mit allen Feldern senden , sondern nur das Adressfeld über die PATCH-Methode aktualisieren.

Wir können ein HeavyResourceAddressOnly- DTO erstellen , um eine teilweise Aktualisierung des Adressfelds darzustellen:

public class HeavyResourceAddressOnly { private Integer id; private String address; // ... }

Als nächstes können wir die PATCH-Methode nutzen, um eine teilweise Aktualisierung zu senden:

@PatchMapping("/heavyresource/{id}") public ResponseEntity partialUpdateName( @RequestBody HeavyResourceAddressOnly partialUpdate, @PathVariable("id") String id) { heavyResourceRepository.save(partialUpdate, id); return ResponseEntity.ok("resource address updated"); }

Mit diesem detaillierteren DTO können wir das Feld senden, das wir nur aktualisieren müssen - ohne den Aufwand für das Senden der gesamten HeavyResource .

Wenn wir eine große Anzahl dieser Teilaktualisierungsvorgänge haben, können wir auch die Erstellung eines benutzerdefinierten DTO für jeden Ausgang überspringen - und nur eine Karte verwenden:

@RequestMapping(value = "/heavyresource/{id}", method = RequestMethod.PATCH, consumes = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity partialUpdateGeneric( @RequestBody Map updates, @PathVariable("id") String id) { heavyResourceRepository.save(updates, id); return ResponseEntity.ok("resource updated"); }

Diese Lösung bietet uns mehr Flexibilität bei der Implementierung der API. Wir verlieren jedoch auch einige Dinge - wie die Validierung.

4. Testen von PUT und PATCH

Lassen Sie uns abschließend Tests für beide HTTP-Methoden schreiben. Zunächst möchten wir die Aktualisierung der vollständigen Ressource über die PUT-Methode testen:

mockMvc.perform(put("/heavyresource/1") .contentType(MediaType.APPLICATION_JSON_VALUE) .content(objectMapper.writeValueAsString( new HeavyResource(1, "Tom", "Jackson", 12, "heaven street"))) ).andExpect(status().isOk());

Die Ausführung einer Teilaktualisierung wird mithilfe der PATCH-Methode erreicht:

mockMvc.perform(patch("/heavyrecource/1") .contentType(MediaType.APPLICATION_JSON_VALUE) .content(objectMapper.writeValueAsString( new HeavyResourceAddressOnly(1, "5th avenue"))) ).andExpect(status().isOk());

Wir können auch einen Test für einen allgemeineren Ansatz schreiben:

HashMap updates = new HashMap(); updates.put("address", "5th avenue"); mockMvc.perform(patch("/heavyresource/1") .contentType(MediaType.APPLICATION_JSON_VALUE) .content(objectMapper.writeValueAsString(updates)) ).andExpect(status().isOk()); 

5. Handhabung Partial Requests mit Null Values

Wenn wir eine Implementierung für eine PATCH-Methode schreiben, müssen wir einen Vertrag zur Behandlung von Fällen angeben, in denen null als Wert für das Adressfeld in HeavyResourceAddressOnly angezeigt wird.

Angenommen, der Client sendet die folgende Anfrage:

{ "id" : 1, "address" : null }

Dann können wir dies so behandeln, dass ein Wert des Adressfelds auf null gesetzt wird oder eine solche Anforderung einfach ignoriert wird, indem sie als unverändert behandelt wird.

Wir sollten eine Strategie für den Umgang mit Null auswählen und uns bei jeder Implementierung der PATCH-Methode daran halten.

6. Fazit

In diesem kurzen Tutorial haben wir uns darauf konzentriert, die Unterschiede zwischen den Methoden HTTP PATCH und PUT zu verstehen.

Wir haben einen einfachen Spring REST-Controller implementiert, um eine Ressource über die PUT-Methode und eine teilweise Aktualisierung mit PATCH zu aktualisieren.

Die Implementierung all dieser Beispiele und Codefragmente finden Sie im GitHub-Projekt - dies ist ein Maven-Projekt, daher sollte es einfach zu importieren und auszuführen sein, wie es ist.