Для чего нужен HTTP метод PATCH?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
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, позволяющий снизить трафик и упростить взаимодействие с сервером.