1. введение
Из различных доступных методов HTTP метод HTTP PATCH играет уникальную роль. Это позволяет нам применять частичные обновления к ресурсам HTTP.
В этом руководстве мы рассмотрим, как использовать метод HTTP PATCH вместе с форматом документа JSON Patch для применения частичных обновлений к нашим ресурсам RESTful.
2. Пример Использования
Давайте начнем с рассмотрения примера HTTP Customer resource, представленного документом JSON:
{ "id":"1", "telephone":"001-555-1234", "favorites":["Milk","Eggs"], "communicationPreferences": {"post":true, "email":true} }
Предположим, что номер телефона этого клиента изменился и что клиент добавил новый элемент в свой список любимых продуктов. Это означает, что нам нужно обновить только телефон и избранное поля Клиента .
Как бы мы это сделали?
Первым на ум приходит популярный метод HTTP PUT. Однако, поскольку PUT полностью заменяет ресурс, это не подходящий метод для элегантного применения частичных обновлений. Кроме того, клиенты должны выполнить GET, прежде чем обновления будут применены и сохранены.
Именно здесь пригодится метод HTTP PATCH .
Давайте разберемся в методе HTTP – ПАТЧА и форматах патчей JSON.
3. Метод HTTP-ПАТЧА и формат патча JSON
Метод HTTP PATCH предлагает хороший способ применить частичные обновления к ресурсам. В результате клиенты должны отправлять только различия в своих запросах.
Давайте рассмотрим простой пример запроса на исправление HTTP:
PATCH /customers/1234 HTTP/1.1 Host: www.example.com Content-Type: application/example If-Match: "e0023aa4e" Content-Length: 100 [description of changes]
Тело запроса на исправление HTTP описывает, как целевой ресурс должен быть изменен для создания новой версии. Кроме того, формат, используемый для представления [описание изменений] , варьируется в зависимости от типа ресурса. Для типов ресурсов JSON для описания изменений используется формат JSON Patch .
Проще говоря, формат исправления JSON использует “серию операций” для описания того, как должен быть изменен целевой ресурс. Документ исправления JSON – это массив объектов JSON. Каждый объект в массиве представляет собой ровно одну операцию исправления JSON.
Давайте теперь рассмотрим операции исправления JSON вместе с некоторыми примерами.
4. Операции исправления JSON
Операция исправления JSON представлена одним объектом op .
Например, здесь мы определяем операцию исправления JSON для обновления телефонного номера клиента:
{ "op":"replace", "path":"/telephone", "value":"001-555-5678" }
Каждая операция должна иметь один член path . Кроме того, некоторые объекты операций также должны содержать элемент from . Значение path и from членов является указателем JSON . Он ссылается на местоположение в целевом документе. Это местоположение может указывать на определенный ключ или элемент массива в целевом объекте.
Теперь давайте кратко рассмотрим доступные операции исправления JSON.
4.1. Операция добавления
Мы используем операцию add для добавления нового элемента в объект. Кроме того, мы можем использовать его для обновления существующего элемента и вставки нового значения в массив по указанному индексу.
Например, давайте добавим “Хлеб” в список избранное клиента с индексом 0:
{ "op":"add", "path":"/favorites/0", "value":"Bread" }
Измененные данные клиента после операции add будут:
{ "id":"1", "telephone":"001-555-1234", "favorites":["Bread","Milk","Eggs"], "communicationPreferences": {"post":true, "email":true} }
4.2. Операция удаления
Операция remove удаляет значение в целевом местоположении. Кроме того, он может удалить элемент из массива с указанным индексом.
Например, давайте удалим коммуникационные предпочтения для нашего клиента:
{ "op":"remove", "path":"/communicationPreferences" }
Измененные данные клиента после операции remove будут:
{ "id":"1", "telephone":"001-555-1234", "favorites":["Bread","Milk","Eggs"], "communicationPreferences":null }
4.3. Операция замены
Операция replace обновляет значение в целевом местоположении новым значением.
В качестве примера давайте обновим номер телефона нашего клиента:
{ "op":"replace", "path":"/telephone", "value":"001-555-5678" }
Измененные данные клиента после операции replace будут:
{ "id":"1", "telephone":"001-555-5678", "favorites":["Bread","Milk","Eggs"], "communicationPreferences":null }
4.4. Операция перемещения
Операция move удаляет значение в указанном местоположении и добавляет его в целевое местоположение.
Например, давайте переместим “Хлеб” из верхней части списка избранное клиента в нижнюю часть списка:
{ "op":"move", "from":"/favorites/0", "path":"/favorites/-" }
Измененные данные клиента после операции move будут:
{ "id":"1", "telephone":"001-555-5678", "favorites":["Milk","Eggs","Bread"], "communicationPreferences":null }
/избранное/0 и /избранное/- в приведенном выше примере являются указателями JSON на начальный и конечный индексы массива избранное .
4.5. Операция копирования
Операция copy копирует значение из указанного местоположения в целевое местоположение.
Например, давайте продублируем “Молоко” в списке избранное :
{ "op":"copy", "from":"/favorites/0", "path":"/favorites/-" }
Измененные данные клиента после операции copy будут:
{ "id":"1", "telephone":"001-555-5678", "favorites":["Milk","Eggs","Bread","Milk"], "communicationPreferences":null }
4.6. Испытательная Операция
Операция test проверяет, что значение в “пути” равно “значению”. Поскольку операция ИСПРАВЛЕНИЯ является атомарной, исправление должно быть отброшено, если какая-либо из его операций завершится неудачей. Операция test может использоваться для проверки выполнения предварительных и постусловий.
Например, давайте проверим, что обновление поля телефон клиента прошло успешно:
{ "op":"test", "path":"/telephone", "value":"001-555-5678" }
Давайте теперь посмотрим, как мы можем применить вышеприведенные концепции к нашему примеру.
5. HTTP-запрос ПАТЧА с использованием формата патча JSON
Мы вернемся к нашему Клиенту варианту использования.
Вот HTTP-запрос на ИСПРАВЛЕНИЕ для выполнения частичного обновления списка телефон и избранное клиента с использованием формата исправления JSON:
curl -i -X PATCH http://localhost:8080/customers/1 -H "Content-Type: application/json-patch+json" -d '[ {"op":"replace","path":"/telephone","value":"+1-555-56"}, {"op":"add","path":"/favorites/0","value":"Bread"} ]'
Самое главное, что Тип контента для запросов на исправление JSON это приложение/json-патч+json . Кроме того, тело запроса представляет собой массив объектов операции исправления JSON:
[ {"op":"replace","path":"/telephone","value":"+1-555-56"}, {"op":"add","path":"/favorites/0","value":"Bread"} ]
Как бы мы обработали такой запрос на стороне сервера?
Один из способов-написать пользовательскую структуру, которая последовательно оценивает операции и применяет их к целевому ресурсу как атомарную единицу. Очевидно, что такой подход звучит сложно. Кроме того, это может привести к нестандартному способу использования документов исправлений.
К счастью, нам не нужно вручную обрабатывать запросы на исправление JSON.
API Java для обработки JSON 1.0 или JSONP 1.0, первоначально определенный в JSR 353 , представил поддержку исправления JSON в JSR 374 . API JSON-P предоставляет тип Json Patch для представления реализации патча JSON.
Однако JSONP-это всего лишь API. Для работы с API JSONP нам необходимо использовать библиотеку, которая его реализует. Мы будем использовать одну из таких библиотек под названием json-patch для примеров в этой статье.
Давайте теперь рассмотрим, как мы можем создать службу REST, которая использует HTTP-запросы на исправление, используя формат исправления JSON, описанный выше.
6. Реализация исправления JSON в приложении Spring Boot
6.1. Зависимости
Последнюю версию json-patch можно найти в центральном репозитории Maven.
Для начала давайте добавим зависимости в pom.xml :
com.github.java-json-tools json-patch 1.12
Теперь давайте определим класс схемы для представления документа Customer JSON:
public class Customer { private String id; private String telephone; private Listfavorites; private Map communicationPreferences; // standard getters and setters }
Далее мы рассмотрим наш метод контроллера.
6.2. Метод контроллера ПОКОЯ
Затем мы можем реализовать HTTP-ПАТЧ для нашего случая использования клиентом:
@PatchMapping(path = "/{id}", consumes = "application/json-patch+json") public ResponseEntityupdateCustomer(@PathVariable String id, @RequestBody JsonPatch patch) { try { Customer customer = customerService.findCustomer(id).orElseThrow(CustomerNotFoundException::new); Customer customerPatched = applyPatchToCustomer(patch, customer); 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(); } }
Давайте теперь разберемся, что происходит в этом методе:
- Для начала мы используем аннотацию @Pathmapping , чтобы пометить метод как метод обработчика исправлений
- Когда поступает запрос на исправление с application/json-patch+json “Content-Type”, Spring Boot использует по умолчанию MappingJackson2HttpMessageConverter для преобразования полезной нагрузки запроса в экземпляр JsonPatch . В результате наш метод контроллера получит тело запроса в виде экземпляра Json Patch
В рамках метода:
- Во-первых, мы вызываем службу поддержки клиентов.найти клиента(id) метод поиска записи клиента
- Впоследствии, если запись клиента найдена, мы вызываем метод применить исправление к Клиенту(patch, customer) . Это применяет патч Json к клиенту (подробнее об этом позже)
- Затем мы вызываем CustomerService.UpdateCustomer(клиент исправлен) для сохранения записи клиента
- Наконец, мы возвращаем 200 OK ответ клиенту с исправленной Информацией о клиенте в ответе
Самое главное, что настоящая магия происходит в методе применить патч к клиенту(patch, customer) :
private Customer applyPatchToCustomer( JsonPatch patch, Customer targetCustomer) throws JsonPatchException, JsonProcessingException { JsonNode patched = patch.apply(objectMapper.convertValue(targetCustomer, JsonNode.class)); return objectMapper.treeToValue(patched, Customer.class); }
- Начнем с того, что у нас есть экземпляр Jsonpath , который содержит список операций, которые должны быть применены к целевому Клиенту
- Затем мы преобразуем целевой Клиент в экземпляр com.fasterxml.jackson.databind.JsonNode и передайте его методу JsonPatch.apply для применения исправления. За кулисами JsonPatch.apply занимается применением операций к цели. Результатом исправления также является com.fasterxml.jackson.databind.JsonNode экземпляр
- Затем мы вызываем метод ObjectMapper.treeToValue , который связывает данные в исправленном com.fasterxml.jackson.databind.JsonNode к типу Customer . Это наш исправленный Клиент экземпляр
- Наконец, мы возвращаем исправленный экземпляр Customer
Теперь давайте проведем несколько тестов с нашим API.
6.3. Тестирование
Для начала давайте создадим клиента, используя POST-запрос к нашему API:
curl -i -X POST http://localhost:8080/customers -H "Content-Type: application/json" -d '{"telephone":"+1-555-12","favorites":["Milk","Eggs"],"communicationPreferences":{"post":true,"email":true}}'
Мы получаем 201 Созданный ответ:
HTTP/1.1 201 Location: http://localhost:8080/customers/1
Заголовок Location response устанавливается в положение нового ресурса. Он указывает, что id нового Клиента равен 1.
Затем давайте запросим частичное обновление для этого клиента с помощью запроса на ИСПРАВЛЕНИЕ:
curl -i -X PATCH http://localhost:8080/customers/1 -H "Content-Type: application/json-patch+json" -d '[ {"op":"replace","path":"/telephone","value":"+1-555-56"}, {"op":"add","path":"/favorites/0","value": "Bread"} ]'
Мы получаем 200 OK ответ с исправленными данными клиента:
HTTP/1.1 200 Content-Type: application/json Transfer-Encoding: chunked Date: Fri, 14 Feb 2020 21:23:14 GMT {"id":"1","telephone":"+1-555-56","favorites":["Bread","Milk","Eggs"],"communicationPreferences":{"post":true,"email":true}}
7. Заключение
В этой статье мы рассмотрели, как реализовать патч JSON в API Spring REST.
Для начала мы рассмотрели метод HTTP PATCH и его способность выполнять частичные обновления.
Затем мы рассмотрели, что такое патч JSON, и поняли различные операции с патчем JSON.
Наконец, мы обсудили, как обрабатывать HTTP-запрос на исправление в приложении Spring Boot с помощью библиотеки json-patch.
Как всегда, исходный код примеров, используемых в этой статье, доступен на GitHub .