← Назад к вопросам

Для чего нужен HTTP метод PATCH?

2.0 Middle🔥 161 комментариев
#REST API и микросервисы

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

HTTP метод PATCH: частичное обновление ресурсов

PATCH — это HTTP метод, предназначенный для частичного обновления ресурса. В отличие от PUT, который требует отправить полный ресурс, PATCH позволяет отправить только изменения (дельту) и часто используется для экономии трафика и упрощения клиентского кода.

PATCH vs PUT: ключевые отличия

PUT (полное замещение):

  • Требует отправить ВЕСЬ ресурс
  • Заменяет ресурс полностью
  • Если отправить неполные данные, недостающие поля удаляются
  • Идемпотентна (много вызовов = один вызов)
// PUT запрос: отправляем полный объект
PUT /api/users/123
Content-Type: application/json

{
  "name": "John Doe",
  "email": "john@example.com",
  "age": 30,
  "phone": "+1234567890"
}
// Весь объект заменяется на отправленный

PATCH (частичное обновление):

  • Отправляем ТОЛЬКО измененные поля
  • Не затрагиваем другие данные
  • Экономит трафик
  • Может быть не идемпотентна (зависит от реализации)
// PATCH запрос: отправляем только изменения
PATCH /api/users/123
Content-Type: application/json

{
  "email": "newemail@example.com",
  "age": 31
}
// Только email и age обновляются, остальное не трогается

Практический пример в Spring Boot

Модель данных:

@Entity
@Data
@NoArgsConstructor
public class User {
  @Id
  private Long id;
  private String name;
  private String email;
  private Integer age;
  private String phone;
  private String address;
  private LocalDateTime createdAt;
}

PATCH контроллер:

@RestController
@RequestMapping("/api/users")
public class UserController {
  @Autowired
  private UserService userService;
  
  // PUT: полное обновление
  @PutMapping("/{id}")
  public ResponseEntity<UserDto> updateUserFull(
      @PathVariable Long id,
      @RequestBody @Valid UserUpdateDto request) {
    User updatedUser = userService.updateUserFull(id, request);
    return ResponseEntity.ok(new UserDto(updatedUser));
  }
  
  // PATCH: частичное обновление
  @PatchMapping("/{id}")
  public ResponseEntity<UserDto> updateUserPartial(
      @PathVariable Long id,
      @RequestBody Map<String, Object> updates) {
    User updatedUser = userService.updateUserPartial(id, updates);
    return ResponseEntity.ok(new UserDto(updatedUser));
  }
}

Сервис с логикой PATCH:

@Service
public class UserService {
  @Autowired
  private UserRepository userRepository;
  
  // Способ 1: используя Map<String, Object>
  public User updateUserPartial(Long id, Map<String, Object> updates) {
    User user = userRepository.findById(id)
      .orElseThrow(() -> new ResourceNotFoundException("User not found"));
    
    // Безопасное обновление только переданных полей
    updates.forEach((key, value) -> {
      switch (key) {
        case "name":
          user.setName((String) value);
          break;
        case "email":
          user.setEmail((String) value);
          break;
        case "age":
          user.setAge((Integer) value);
          break;
        case "phone":
          user.setPhone((String) value);
          break;
        case "address":
          user.setAddress((String) value);
          break;
        default:
          throw new IllegalArgumentException("Unknown field: " + key);
      }
    });
    
    return userRepository.save(user);
  }
}

Использование Jackson для PATCH

Более элегантный подход с Jackson:

@Service
public class UserService {
  @Autowired
  private UserRepository userRepository;
  
  @Autowired
  private ObjectMapper objectMapper;
  
  public User updateUserPartial(Long id, Map<String, Object> updates) {
    User user = userRepository.findById(id)
      .orElseThrow(() -> new ResourceNotFoundException("User not found"));
    
    // Jackson вспомогательный метод для merge
    User updatedUser = objectMapper.updateValue(user, updates);
    
    return userRepository.save(updatedUser);
  }
}

JSON Patch стандарт (RFC 6902)

Для сложных PATCH операций существует стандарт JSON Patch:

// JSON Patch запрос
PATCH /api/users/123
Content-Type: application/json-patch+json

[
  { "op": "replace", "path": "/email", "value": "new@example.com" },
  { "op": "replace", "path": "/age", "value": 31 },
  { "op": "add", "path": "/tags", "value": ["admin", "verified"] },
  { "op": "remove", "path": "/phone" }
]

Реализация JSON Patch в Spring:

@PatchMapping("/{id}", consumes = "application/json-patch+json")
public ResponseEntity<UserDto> patchUser(
    @PathVariable Long id,
    @RequestBody JsonPatch patch) {
  try {
    User user = userRepository.findById(id)
      .orElseThrow(() -> new ResourceNotFoundException("User not found"));
    
    // Преобразуем User в JsonNode
    JsonNode patched = patch.apply(objectMapper.convertValue(user, JsonNode.class));
    
    // Преобразуем обратно в User
    User patchedUser = objectMapper.treeToValue(patched, User.class);
    
    User savedUser = userRepository.save(patchedUser);
    return ResponseEntity.ok(new UserDto(savedUser));
  } catch (JsonPatchException e) {
    return ResponseEntity.badRequest().build();
  }
}

JSON Merge Patch (RFC 7386)

Альтернативный, более простой стандарт:

// JSON Merge Patch запрос
PATCH /api/users/123
Content-Type: application/merge-patch+json

{
  "email": "new@example.com",
  "age": 31,
  "phone": null  // null означает "удалить поле"
}

PATCH в клиентском коде (Java HTTP Client)

public class UserClient {
  public void patchUser(Long userId, Map<String, Object> updates) throws Exception {
    HttpClient client = HttpClient.newHttpClient();
    
    String json = objectMapper.writeValueAsString(updates);
    
    HttpRequest request = HttpRequest.newBuilder()
      .uri(URI.create("https://api.example.com/users/" + userId))
      .method("PATCH", HttpRequest.BodyPublishers.ofString(json))
      .header("Content-Type", "application/json")
      .header("Authorization", "Bearer " + token)
      .build();
    
    HttpResponse<String> response = client.send(request,
      HttpResponse.BodyHandlers.ofString());
    
    System.out.println("Status: " + response.statusCode());
    System.out.println("Body: " + response.body());
  }
}

PATCH с RestTemplate (Spring)

@Service
public class UserApiClient {
  @Autowired
  private RestTemplate restTemplate;
  
  public UserDto patchUser(Long userId, Map<String, Object> updates) {
    // RestTemplate не поддерживает PATCH напрямую,
    // используем exchange с методом PATCH
    HttpHeaders headers = new HttpHeaders();
    headers.setContentType(MediaType.APPLICATION_JSON);
    
    HttpEntity<Map<String, Object>> entity = 
      new HttpEntity<>(updates, headers);
    
    ResponseEntity<UserDto> response = restTemplate.exchange(
      "https://api.example.com/users/" + userId,
      HttpMethod.PATCH,
      entity,
      UserDto.class
    );
    
    return response.getBody();
  }
}

Когда использовать PATCH

✅ Используй PATCH когда:

  • Клиент может отправить только изменённые поля
  • Важно сэкономить трафик (мобильные клиенты)
  • API часто обновляет отдельные поля
  • Хочешь избежать race conditions (PUT требует знать состояние)

❌ Избегай PATCH когда:

  • Логика требует атомарности всего ресурса
  • Нужна простота и строгие гарантии идемпотентности
  • Работаешь с legacy системами, не поддерживающими PATCH

Сравнение методов

МетодПолное обновлениеЧастичноеИдемпотентнаБезопасная
PUT
POST
PATCH

PATCH метод — это современный подход для эффективного обновления ресурсов в REST API, позволяющий снизить трафик и упростить взаимодействие с сервером.