JSON-patch gebruiken in Spring REST API's

1. Inleiding

Van de verschillende beschikbare HTTP-methoden speelt de HTTP PATCH-methode een unieke rol. Het stelt ons in staat om gedeeltelijke updates toe te passen op HTTP-bronnen.

In deze zelfstudie bekijken we hoe u de HTTP PATCH-methode samen met de JSON Patch-documentindeling kunt gebruiken om gedeeltelijke updates toe te passen op onze RESTful-bronnen.

2. De use case

Laten we beginnen met een voorbeeld van HTTP Klant resource vertegenwoordigd door het JSON-document:

{"id": "1", "phone": "001-555-1234", "favourites": ["Milk", "Eggs"], "communicationPreferences": {"post": true, "email": true}}

Laten we aannemen dat het telefoonnummer van deze klantis veranderd en dat de klant een nieuw item aan zijn lijst met favoriete producten heeft toegevoegd. Dit betekent dat we alleen het telefoon en favorieten velden van de Klant.

Hoe zouden we dat doen?

De populaire HTTP PUT-methode komt als eerste in me op. Omdat de PUT een bron volledig vervangt, is het echter geen geschikte methode om gedeeltelijke updates elegant toe te passen. Bovendien moeten de clients een GET uitvoeren voordat de updates worden toegepast en opgeslagen.

Dit is waar de HTTP PATCH-methode van pas komt.

Laten we de HTTP PATCH-methode en de JSON Patch-indelingen begrijpen.

3. De HTTP PATCH-methode en het JSON-patchformaat

De HTTP PATCH-methode biedt een handige manier om gedeeltelijke updates op bronnen toe te passen. Als gevolg hiervan hoeven klanten alleen de verschillen in hun verzoeken te verzenden.

Laten we eens kijken naar een eenvoudig voorbeeld van een HTTP PATCH-verzoek:

PATCH / klanten / 1234 HTTP / 1.1 Host: www.example.com Inhoudstype: applicatie / voorbeeld If-Match: "e0023aa4e" Inhoudslengte: 100 [beschrijving van wijzigingen]

De hoofdtekst van het HTTP PATCH-verzoek beschrijft hoe de doelbron moet worden gewijzigd om een ​​nieuwe versie te produceren. Bovendien is het formaat dat wordt gebruikt om het [beschrijving van wijzigingen] varieert afhankelijk van het brontype. Voor JSON-resourcetypen is de indeling die wordt gebruikt om de wijzigingen te beschrijven, JSON-patch.

Simpel gezegd, het JSON Patch-formaat gebruikt een "reeks bewerkingen" om te beschrijven hoe de doelbron moet worden gewijzigd. Een JSON-patchdocument is een reeks JSON-objecten. Elk object in de array vertegenwoordigt precies één JSON-patchbewerking.

Laten we nu eens kijken naar de JSON Patch-bewerkingen samen met enkele voorbeelden.

4. JSON-patchbewerkingen

Een JSON-patchbewerking wordt vertegenwoordigd door een enkele op voorwerp.

Hier definiëren we bijvoorbeeld een JSON-patchbewerking om het telefoonnummer van de klant bij te werken:

{"op": "replace", "path": "/ phone", "value": "001-555-5678"}

Elke bewerking moet er een hebben pad lid. Sommige bewerkingsobjecten moeten ook een van lid ook. De waarde van de pad en van members is een JSON Pointer. Het verwijst naar een locatie binnen het doeldocument. Deze locatie kan verwijzen naar een specifieke sleutel of een array-element in het doelobject.

Laten we nu kort kijken naar de beschikbare JSON-patchbewerkingen.

4.1. De toevoegen Operatie

Wij gebruiken de toevoegen bewerking om een ​​nieuw lid aan een object toe te voegen. We kunnen het ook gebruiken om een ​​bestaand lid bij te werken en om een ​​nieuwe waarde in de array op de opgegeven index in te voegen.

Laten we bijvoorbeeld "Brood" toevoegen aan dat van de klant favorieten lijst bij index 0:

{"op": "add", "path": "/ favourites / 0", "value": "Brood"}

De gewijzigde klantgegevens na de toevoegen operatie zou zijn:

{"id": "1", "phone": "001-555-1234", "favourites": ["Bread", "Milk", "Eggs"], "communicationPreferences": {"post": true, "email": true}}

4.2. De verwijderen Operatie

De verwijderen operatie verwijdert een waarde op de doellocatie. Bovendien kan het een element uit een array met de opgegeven index verwijderen.

Laten we bijvoorbeeld het communcationPreferences voor onze klant:

{"op": "remove", "path": "/ communicationPreferences"}

De gewijzigde klantgegevens na de verwijderen operatie zou zijn:

{"id": "1", "phone": "001-555-1234", "favourites": ["Bread", "Milk", "Eggs"], "communicationPreferences": null}

4.3. De vervangen Operatie

De vervangen operatie werkt de waarde op de doellocatie bij met een nieuwe waarde.

Laten we als voorbeeld het telefoonnummer van onze klant bijwerken:

{"op": "replace", "path": "/ phone", "value": "001-555-5678"}

De gewijzigde klantgegevens na de vervangen operatie zou zijn:

{"id": "1", "phone": "001-555-5678", "favourites": ["Brood", "Melk", "Eieren"], "communicationPreferences": null}

4.4. De Actie Operatie

De Actie operatie verwijdert de waarde op de opgegeven locatie en voegt deze toe aan de doellocatie.

Laten we bijvoorbeeld "Brood" van de bovenkant van de klant verplaatsen favorieten lijst onderaan de lijst:

{"op": "move", "from": "/ favourites / 0", "path": "/ favourites / -"}

De gewijzigde klantgegevens na de Actie operatie zou zijn:

{"id": "1", "phone": "001-555-5678", "favourites": ["Milk", "Eggs", "Bread"], "communicationPreferences": null} 

De / favorieten / 0 en / favorieten / - in het bovenstaande voorbeeld zijn JSON-verwijzingen naar de begin- en eindindices van de favorieten array.

4.5. De kopiëren Operatie

De kopiëren operatie kopieert de waarde op de opgegeven locatie naar de doellocatie.

Laten we bijvoorbeeld "Melk" dupliceren in het favorieten lijst:

{"op": "copy", "from": "/ favourites / 0", "path": "/ favourites / -"}

De gewijzigde klantgegevens na de kopiëren operatie zou zijn:

{"id": "1", "phone": "001-555-5678", "favourites": ["Milk", "Eggs", "Bread", "Milk"], "communicationPreferences": null}

4.6. De test Operatie

De test operatie test of de waarde op het "pad" gelijk is aan de "waarde". Omdat de PATCH-bewerking atomair is, moet de PATCH worden weggegooid als een van de bewerkingen mislukt. De test operatie kan worden gebruikt om te valideren dat aan de rand- en postvoorwaarden is voldaan.

Laten we bijvoorbeeld testen of de update van de klant telefoon veld is succesvol geweest:

{"op": "test", "path": "/ phone", "value": "001-555-5678"} 

Laten we nu kijken hoe we de bovenstaande concepten op ons voorbeeld kunnen toepassen.

5. HTTP PATCH-aanvraag met behulp van het JSON-patchformaat

We zullen onze opnieuw bezoeken Klant use case.

Hier is het HTTP PATCH-verzoek om een ​​gedeeltelijke update van het telefoon en favorieten lijst met behulp van het JSON-patchformaat:

curl -i -X ​​PATCH // localhost: 8080 / klanten / 1 -H "Content-Type: application / json-patch + json" -d '[{"op": "replace", "path": "/ phone "," value ":" + 1-555-56 "}, {" op ":" add "," path ":" / favourites / 0 "," value ":" Brood "}] ' 

Het belangrijkste is dat de Inhoudstype voor JSON-patchverzoeken is applicatie / json-patch + json. De hoofdtekst van het verzoek is ook een array van JSON-patchbewerkingsobjecten:

[{"op": "replace", "path": "/ phone", "value": "+ 1-555-56"}, {"op": "add", "path": "/ favorieten / 0 "," value ":" Brood "}]

Hoe zouden we een dergelijk verzoek aan de serverzijde verwerken?

Een manier is om een ​​aangepast raamwerk te schrijven dat de bewerkingen opeenvolgend evalueert en ze als een atomaire eenheid toepast op de doelbron. Deze benadering klinkt duidelijk ingewikkeld. Het kan ook leiden tot een niet-gestandaardiseerde manier om patchdocumenten te gebruiken.

Gelukkig hoeven we de verwerking van JSON-patchverzoeken niet met de hand te maken.

De Java API voor JSON Processing 1.0, of JSON-P 1.0, oorspronkelijk gedefinieerd in JSR 353, introduceerde ondersteuning voor de JSON-patch in JSR 374. De JSON-P API biedt de JsonPatch type om de implementatie van de JSON-patch weer te geven.

JSON-P is echter slechts een API. Om met de JSON-P API te werken, hebben we een bibliotheek nodig die deze implementeert. We gebruiken een dergelijke bibliotheek genaamd json-patch voor de voorbeelden in dit artikel.

Laten we nu kijken hoe we een REST-service kunnen bouwen die HTTP PATCH-verzoeken gebruikt met behulp van het JSON Patch-formaat dat hierboven is beschreven.

6. Implementatie van JSON-patch in een Spring Boot-applicatie

6.1. Afhankelijkheden

De nieuwste versie van json-patch is te vinden in de Maven Central-repository.

Laten we om te beginnen de afhankelijkheden toevoegen aan het pom.xml:

 com.github.java-json-tools json-patch 1.12 

Laten we nu een schemaklasse definiëren om de Klant JSON-document:

openbare klasse Klant {privé String-id; privé String-telefoon; privélijst favorieten; privé kaartcommunicatieVoorkeuren; // standaard getters en setters}

Vervolgens kijken we naar onze controllermethode.

6.2. De REST-controllermethode

Vervolgens kunnen we HTTP PATCH implementeren voor het gebruik van onze klanten:

@PatchMapping (path = "/ {id}", consumes = "application / json-patch + json") openbare ResponseEntity updateCustomer (@PathVariable String id, @RequestBody JsonPatch-patch) {probeer {Customer customer = customerService.findCustomer (id) .orElseThrow (CustomerNotFoundException :: nieuw); Customer customerPatched = applyPatchToCustomer (patch, klant); customerService.updateCustomer (customerPatched); return ResponseEntity.ok (customerPatched); } catch (JsonPatchException | JsonProcessingException e) {return ResponseEntity.status (HttpStatus.INTERNAL_SERVER_ERROR) .build (); } catch (CustomerNotFoundException e) {return ResponseEntity.status (HttpStatus.NOT_FOUND) .build (); }} 

Laten we nu begrijpen wat er aan de hand is in deze methode:

  • Om te beginnen gebruiken we de @PatchMapping annotatie om de methode te markeren als een PATCH-handlermethode
  • Wanneer een patchverzoek met de applicatie / json-patch + json "Content-Type" arriveert, Spring Boot gebruikt de standaardinstelling Toewijzing Jackson2HttpMessageConverter om de payload van het verzoek om te zetten in een JsonPatch voorbeeld. Als gevolg hiervan ontvangt onze controllermethode de aanvraagtekst als een JsonPatch voorbeeld

Binnen de methode:

  1. Ten eerste noemen we de customerService.findCustomer (id) methode om het klantrecord te vinden
  2. Als vervolgens het klantrecord wordt gevonden, roepen we het applyPatchToCustomer (patch, klant) methode. Dit is van toepassing op de JsonPatch aan de klant (hierover later meer)
  3. We roepen dan de customerService.updateCustomer (customerPatched) om het klantrecord op te slaan
  4. Ten slotte retourneren we een 200 OK reactie op de cliënt met de gepatchte Klant details in het antwoord

Het belangrijkste is dat de echte magie plaatsvindt in de applyPatchToCustomer (patch, klant) methode:

private Customer applyPatchToCustomer (JsonPatch-patch, klant targetCustomer) gooit JsonPatchException, JsonProcessingException {JsonNode patched = patch.apply (objectMapper.convertValue (targetCustomer, JsonNode.class)); return objectMapper.treeToValue (gepatcht, Customer.class); } 
  1. Om te beginnen hebben we onze JsonPatch instantie die de lijst met bewerkingen bevat die op het doel moeten worden toegepast Klant
  2. We converteren vervolgens het doelwit Klant in een instantie van com.fasterxml.jackson.databind.JsonNode en geef het door aan de JsonPatch.apply methode om de patch toe te passen. Achter de schermen is het JsonPatch.apply behandelt het toepassen van de operaties op het doel. Het resultaat van de patch is ook een com.fasterxml.jackson.databind.JsonNode voorbeeld
  3. We noemen dan de objectMapper.treeToValue methode, die de gegevens in het gepatchte com.fasterxml.jackson.databind.JsonNode naar de Klant type. Dit is onze patch Klant voorbeeld
  4. Ten slotte retourneren we de gepatchte Klant voorbeeld

Laten we nu enkele tests uitvoeren met onze API.

6.3. Testen

Laten we om te beginnen een klant maken met behulp van een POST-verzoek aan onze API:

curl -i -X ​​POST // localhost: 8080 / klanten -H "Content-Type: application / json" -d '{"phone": "+ 1-555-12", "favourites": ["Milk", "Eieren"], "communicationPreferences": {"post": true, "email": true}} ' 

We krijgen een 201 Gemaakt reactie:

HTTP / 1.1 201 Locatie: // localhost: 8080 / klanten / 1 

De Plaats antwoordheader wordt ingesteld op de locatie van de nieuwe bron. Het geeft aan dat de ID kaart van het nieuwe Klant is 1.

Laten we vervolgens een gedeeltelijke update voor deze klant aanvragen met behulp van een PATCH-verzoek:

curl -i -X ​​PATCH // localhost: 8080 / klanten / 1 -H "Content-Type: application / json-patch + json" -d '[{"op": "replace", "path": "/ phone "," value ":" + 1-555-56 "}, {" op ":" add "," path ":" / favourites / 0 "," value ":" Brood "}] '

We krijgen een 200OK antwoord met de gepatchte klantgegevens:

HTTP / 1.1 200 Content-Type: application / json Transfer-Encoding: chunked Datum: vr, 14 feb 2020 21:23:14 GMT {"id": "1", "phone": "+ 1-555-56" , "favorieten": ["Brood", "Melk", "Eieren"], "communicationPreferences": {"post": true, "email": true}}

7. Conclusie

In dit artikel hebben we gekeken hoe JSON-patch in Spring REST API's kan worden geïmplementeerd.

Om te beginnen hebben we gekeken naar de HTTP PATCH-methode en de mogelijkheid om gedeeltelijke updates uit te voeren.

We hebben toen gekeken naar wat JSON-patch is en hebben de verschillende JSON-patchbewerkingen begrepen.

Ten slotte hebben we besproken hoe een HTTP PATCH-verzoek in een Spring Boot-toepassing kan worden afgehandeld met behulp van de json-patch-bibliotheek.

Zoals altijd is de broncode voor de voorbeelden die in dit artikel worden gebruikt, beschikbaar op GitHub.


$config[zx-auto] not found$config[zx-overlay] not found