HTTP PUT vs HTTP PATCH in einer REST API

Übersicht

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

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

Wann Put verwenden und wann Patch?

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 ein partielles Update 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 zu berücksichtigen ist, ist Idempotenz; PUT ist idempotent; PATCH kann sein, muss es aber nicht. Abhängig von der Semantik der Operation, die wir implementieren, können wir auch die eine oder andere basierend auf dieser Eigenschaft auswählen.

Implementieren von PUT- und PATCH-Logik

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 mit PUT:

@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 aktualisieren – über die PATCH-Methode.

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 ein partielles Update 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 nur das Feld senden, das wir aktualisieren müssen – ohne den Aufwand, die gesamte HeavyResource zu senden.

Wenn wir eine große Anzahl dieser Teilaktualisierungsvorgänge haben, können wir auch die Erstellung eines benutzerdefinierten DTOs 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<String, Object> updates, @PathVariable("id") String id) { heavyResourceRepository.save(updates, id); return ResponseEntity.ok("resource updated");}

Diese Lösung wird uns mehr Flexibilität bei der Implementierung von API geben; Wir verlieren jedoch auch ein paar Dinge – wie die Validierung.

Testen von PUT und PATCH

Zum Schluss schreiben wir Tests für beide HTTP-Methoden. 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 eines Teilupdates erfolgt mit der PATCH-Methode:

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<String, Object> 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());

Behandlung von Teilanforderungen mit Nullwerten

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

Angenommen, der Client sendet die folgende Anforderung:

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

Dann können wir damit umgehen, indem wir einen Wert des Adressfelds auf null setzen oder eine solche Anforderung einfach ignorieren, indem wir sie als keine Änderung behandeln.

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

Fazit

In dieser Kurzanleitung haben wir uns darauf konzentriert, die Unterschiede zwischen den HTTP-PATCH- und PUT-Methoden zu verstehen.

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

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

Erste Schritte mit Spring 5 und Spring Boot 2 über den Learn Spring-Kurs :

>> SCHAUEN SIE SICH DEN KURS AN

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.