Рубрики
Без рубрики

HTTP PUT vs HTTP PATCH в REST API

Узнайте о разнице между методами HTTP PUT и PATCH и их последствиях при создании API Spring.

Автор оригинала: baeldung.

1. Обзор

В этой краткой статье мы рассмотрим различия между глаголами HTTP PUT и PATCH, а также семантику этих двух операций.

Мы будем использовать Spring для реализации двух конечных точек REST, поддерживающих эти два типа операций, а также для лучшего понимания различий и правильного способа их использования.

2. Когда использовать Put и когда Патч?

Давайте начнем с простого и немного простого утверждения.

Когда клиенту необходимо полностью заменить существующий ресурс, он может использовать PUT. Когда они делают частичное обновление, они могут использовать HTTP-ПАТЧ.

Например, при обновлении одного поля Ресурса отправка полного представления ресурса может быть громоздкой и использовать много ненужной пропускной способности. В таких случаях семантика ПАТЧА имеет гораздо больше смысла.

Еще одним важным аспектом, который следует рассмотреть здесь, является идемпотентность; PUT является идемпотентным; ПАТЧ может быть, но не требуется для . И, таким образом, в зависимости от семантики операции, которую мы реализуем, мы также можем выбрать ту или иную на основе этой характеристики.

3. Реализация логики PUT и PATCH

Допустим, мы хотим реализовать REST API для обновления Тяжелого ресурса с несколькими полями:

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

Во-первых, нам нужно создать конечную точку, которая обрабатывает полное обновление ресурса с помощью PUT:

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

Это стандартная конечная точка для обновления ресурсов.

Теперь предположим, что поле адреса будет часто обновляться клиентом. В этом случае мы не хотим отправлять весь Тяжелый ресурс объект со всеми полями , но мы хотим иметь возможность обновлять только адрес поле – с помощью метода ИСПРАВЛЕНИЯ.

Мы можем создать Только адрес тяжелого ресурса , чтобы представить частичное обновление поля адреса:

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

Затем мы можем использовать метод ИСПРАВЛЕНИЯ для отправки частичного обновления:

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

С помощью этой более детализированной функции мы можем отправить только поле, которое нам нужно обновить, – без накладных расходов на отправку всего HeavyResource .

Если у нас есть большое количество этих операций частичного обновления, мы также можем пропустить создание пользовательского DTO для каждого выхода и использовать только карту:

@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");
}

Это решение даст нам большую гибкость в реализации API; однако мы также теряем некоторые вещи, такие как проверка.

4. Тестирование ПОСТАВЛЕНО и ИСПРАВЛЕНО

Наконец, давайте напишем тесты для обоих методов HTTP. Во-первых, мы хотим протестировать обновление полного ресурса с помощью метода PUT:

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

Выполнение частичного обновления достигается с помощью метода ИСПРАВЛЕНИЯ:

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

Мы также можем написать тест для более общего подхода:

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. Обработка Частичных Запросов С Нулевыми Значениями

Когда мы пишем реализацию для метода ИСПРАВЛЕНИЯ, нам нужно указать контракт о том, как обрабатывать случаи, когда мы получаем null в качестве значения для поля address в HeavyResourceAddressOnly.

Предположим, что клиент отправляет следующий запрос:

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

Затем мы можем обработать это как установку значения поля address в null или просто игнорировать такой запрос, рассматривая его как без изменений.

Мы должны выбрать одну стратегию для обработки null и придерживаться ее в каждой реализации метода ИСПРАВЛЕНИЯ.

6. Заключение

В этом кратком руководстве мы сосредоточились на понимании различий между HTTP-ПАТЧЕМ и методами PUT.

Мы реализовали простой контроллер Spring REST для обновления ресурса с помощью метода PUT и частичного обновления с помощью ПАТЧА.

Реализацию всех этих примеров и фрагментов кода можно найти в проекте GitHub – это проект Maven, поэтому его должно быть легко импортировать и запускать как есть.