Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI28 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
HTTP метод PUT
PUT — это HTTP метод для полной замены ресурса на сервере. Это одна из фундаментальных операций REST API, определённая в RFC 7231.
Основная назначение
PUT используется для:
- Замены всего содержимого ресурса — отправляются все поля
- Создания ресурса с конкретным ID — если ресурса нет, он создаётся
- Полного обновления — старые данные полностью заменяются на новые
Отличие от PATCH
Это часто путают, поэтому важно понять разницу:
// Исходный ресурс (GET /users/1)
{
"id": 1,
"name": "John Doe",
"email": "john@example.com",
"phone": "+1234567890"
}
// PUT — полная замена
// PUT /users/1
{
"name": "Jane Smith",
"email": "jane@example.com",
"phone": "+0987654321"
}
// Результат: полностью заменился, имя изменилось на Jane
// PATCH — частичное обновление
// PATCH /users/1
{
"email": "newemail@example.com"
}
// Результат: только email изменился, остальное осталось
Идемпотентность PUT
PUT идемпотентен — много одинаковых PUT запросов дают тот же результат, что один запрос:
// Первый PUT
PUT /users/1
{
"name": "Alice",
"email": "alice@example.com"
}
Ответ: 200 OK (или 201 Created если был создан)
// Второй PUT (тот же)
PUT /users/1
{
"name": "Alice",
"email": "alice@example.com"
}
Ответ: 200 OK
// Данные остались точно такими же
// Третий PUT (тот же)
Получаем точно то же состояние
Практические примеры
Пример 1: Обновление профиля пользователя
@RestController
@RequestMapping("/api/v1/users")
public class UserController {
@Autowired
private UserService userService;
// PUT — полная замена профиля
@PutMapping("/{id}")
public ResponseEntity<UserDTO> updateUser(
@PathVariable Long id,
@RequestBody UpdateUserRequest request) {
// Берём ВСЕ поля из request и заменяем ВСЕ поля в БД
User user = userService.updateUser(id, request);
return ResponseEntity.ok(UserDTO.from(user));
}
}
public class UpdateUserRequest {
private String name; // Обязательно
private String email; // Обязательно
private String phone; // Обязательно
private String bio; // Обязательно
private String profileImage; // Обязательно
// Все поля должны быть заполнены для PUT
// Если клиент не хочет менять что-то, он отправляет старое значение
}
@Service
public class UserService {
@Transactional
public User updateUser(Long id, UpdateUserRequest request) {
User user = userRepository.findById(id)
.orElseThrow(() -> new UserNotFoundException("User not found"));
// ПОЛНАЯ ЗАМЕНА всех полей
user.setName(request.getName());
user.setEmail(request.getEmail());
user.setPhone(request.getPhone());
user.setBio(request.getBio());
user.setProfileImage(request.getProfileImage());
return userRepository.save(user);
}
}
// REST запрос
PUT /api/v1/users/123 HTTP/1.1
Content-Type: application/json
{
"name": "John Smith",
"email": "john.smith@example.com",
"phone": "+1234567890",
"bio": "Software Engineer",
"profileImage": "https://example.com/john.jpg"
}
// Ответ
200 OK
{
"id": 123,
"name": "John Smith",
"email": "john.smith@example.com",
"phone": "+1234567890",
"bio": "Software Engineer",
"profileImage": "https://example.com/john.jpg"
}
Пример 2: Обновление конфигурации
@RestController
@RequestMapping("/api/v1/settings")
public class SettingsController {
@Autowired
private SettingsService settingsService;
// PUT — заменяем все настройки приложения
@PutMapping("/app-config")
public ResponseEntity<AppConfigDTO> updateAppConfig(
@RequestBody AppConfigRequest request) {
AppConfig config = settingsService.updateConfig(request);
return ResponseEntity.ok(AppConfigDTO.from(config));
}
}
public class AppConfigRequest {
private boolean notificationsEnabled; // Все флаги должны быть в запросе
private int sessionTimeout;
private String theme;
private String language;
private boolean maintenanceMode;
}
// Клиент отправляет все параметры (включая те, которые не меняет)
PUT /api/v1/settings/app-config HTTP/1.1
Content-Type: application/json
{
"notificationsEnabled": true,
"sessionTimeout": 3600,
"theme": "dark",
"language": "en",
"maintenanceMode": false
}
Пример 3: Создание ресурса с конкретным ID
@RestController
@RequestMapping("/api/v1/documents")
public class DocumentController {
@Autowired
private DocumentService documentService;
// PUT может создать ресурс, если его нет
@PutMapping("/{documentId}")
public ResponseEntity<?> upsertDocument(
@PathVariable String documentId,
@RequestBody DocumentRequest request) {
Document document = documentService.createOrUpdate(documentId, request);
return ResponseEntity.ok(document);
}
}
@Service
public class DocumentService {
@Transactional
public Document createOrUpdate(String documentId, DocumentRequest request) {
// Ищем документ
Document document = documentRepository.findById(documentId)
.orElse(null);
if (document == null) {
// Нет — создаём новый с конкретным ID
document = new Document();
document.setId(documentId); // Устанавливаем ID из URL
// Ответ будет 201 Created
}
// else — обновляем существующий (200 OK)
document.setTitle(request.getTitle());
document.setContent(request.getContent());
document.setAuthor(request.getAuthor());
return documentRepository.save(document);
}
}
// Запрос
PUT /api/v1/documents/doc-12345 HTTP/1.1
Content-Type: application/json
{
"title": "My Document",
"content": "Lorem ipsum...",
"author": "John Doe"
}
// Ответ (если был создан)
201 Created
Location: /api/v1/documents/doc-12345
// Ответ (если обновлён)
200 OK
PUT vs POST vs PATCH
| Операция | Метод | Использование | Идемпотентен | Создаёт новый |
|---|---|---|---|---|
| Создание | POST | Когда сервер генерирует ID | Нет | Да |
| Полное обновление | PUT | Замена всего ресурса | Да | Может |
| Частичное обновление | PATCH | Обновление отдельных полей | Нет* | Нет |
| Получение | GET | Чтение | Да | Нет |
| Удаление | DELETE | Удаление | Да | Нет |
Правильное использование PUT
✅ Правильно:
// Все поля отправляются
PUT /api/v1/users/1
{
"name": "Alice",
"email": "alice@example.com",
"age": 28,
"address": "123 Main St"
}
// Или использование вложенного JSON
PUT /api/v1/users/1
{
"profile": {
"name": "Alice",
"email": "alice@example.com"
},
"settings": {
"notifications": true,
"language": "en"
}
}
❌ Неправильно:
// Отправляем только изменённые поля (это PATCH, не PUT)
PUT /api/v1/users/1
{
"email": "newemail@example.com"
}
// Правильно использовать PATCH для этого
// Отправляем ID в теле (уже в URL)
PUT /api/v1/users/1
{
"id": 1, // Лишнее
"name": "Alice"
}
// Отправляем null для удаления полей (может привести к ошибкам)
PUT /api/v1/users/1
{
"name": "Alice",
"phone": null // Лучше явно указать пустое значение
}
HTTP коды ответа для PUT
@PutMapping("/{id}")
public ResponseEntity<?> updateResource(@PathVariable Long id, @RequestBody UpdateRequest request) {
Resource existing = resourceRepository.findById(id).orElse(null);
if (existing == null) {
// Ресурса нет, создаём новый
Resource newResource = new Resource();
newResource.setId(id);
newResource.setData(request.getData());
resourceRepository.save(newResource);
// 201 Created — ресурс был создан
return ResponseEntity
.status(HttpStatus.CREATED)
.location(URI.create("/api/v1/resources/" + id))
.body(newResource);
} else {
// Ресурс существует, обновляем
existing.setData(request.getData());
resourceRepository.save(existing);
// 200 OK — ресурс был обновлён
return ResponseEntity.ok(existing);
}
}
Важное замечание: Null-safety
// Проблема: если клиент не отправил поле, что делать?
public class UserUpdateRequest {
private String name;
private String email;
// Если клиент отправит {"name": "Alice"}, email будет null
}
// Решение 1: Используйте Required аннотации
public class UserUpdateRequest {
@NotBlank(message = "Name is required")
private String name;
@NotBlank(message = "Email is required")
private String email;
}
// Решение 2: Используйте Optional
public class UserUpdateRequest {
private Optional<String> name;
private Optional<String> email;
}
// Решение 3: Используйте PATCH для частичных обновлений
// PUT для полных, PATCH для частичных — это чище
Реальный пример: REST API для конфигурации
@RestController
@RequestMapping("/api/v1/config")
public class ConfigController {
@Autowired
private ConfigService configService;
// GET — получить текущую конфигурацию
@GetMapping("/database")
public ResponseEntity<DatabaseConfig> getDbConfig() {
return ResponseEntity.ok(configService.getDatabaseConfig());
}
// PUT — полностью заменить конфигурацию
@PutMapping("/database")
@Transactional
public ResponseEntity<DatabaseConfig> updateDbConfig(
@RequestBody DatabaseConfigRequest request) {
DatabaseConfig config = new DatabaseConfig();
config.setHost(request.getHost()); // Все поля обновляются
config.setPort(request.getPort());
config.setDatabase(request.getDatabase());
config.setUsername(request.getUsername());
config.setPassword(request.getPassword());
config.setMaxPoolSize(request.getMaxPoolSize());
config.setConnectionTimeout(request.getConnectionTimeout());
configService.updateDatabaseConfig(config);
return ResponseEntity.ok(config);
}
}
public class DatabaseConfigRequest {
@NotBlank
private String host;
@Min(1)
@Max(65535)
private int port;
@NotBlank
private String database;
@NotBlank
private String username;
@NotBlank
private String password;
private int maxPoolSize;
private int connectionTimeout;
// Getters and setters
}
Итог
PUT используется для:
- Полной замены ресурса — все поля должны быть отправлены
- Создания ресурса с конкретным ID — если ресурса нет, он создаётся
- Идемпотентных операций — многократное выполнение = один результат
Не путай с:
- POST — создание новых ресурсов (ID генерирует сервер)
- PATCH — частичное обновление (только изменённые поля)
- DELETE — удаление
PUT — это стандартный метод для REST API обновления полных ресурсов и должен быть идемпотентным для корректной работы распределённых систем.