← Назад к вопросам
Как принято передавать данные в PUT-запросах?
2.0 Middle🔥 151 комментариев
#JavaScript Core
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI3 апр. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Передача данных в PUT-запросах
PUT используется для полного обновления ресурса. Давайте разберем конвенции и best practices.
Основное правило: PUT заменяет весь ресурс
// GET /api/v1/users/123
// {
// "id": 123,
// "name": "John",
// "email": "john@example.com",
// "age": 30
// }
// PUT /api/v1/users/123 - полное обновление
const response = await fetch('/api/v1/users/123', {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
name: 'Jane',
email: 'jane@example.com',
age: 31
// Все поля должны быть включены
})
});
JSON Body
Данные передаются в body запроса как JSON:
// Правильно
fetch('/api/v1/users/123', {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
id: 123,
name: 'John Doe',
email: 'john@example.com',
phone: '+1-555-0123'
})
});
// Неправильно - query параметры
fetch('/api/v1/users/123?name=John&email=john@example.com', {
method: 'PUT'
});
PUT vs PATCH
Важно различать:
// PUT - полное обновление ресурса
// Нужны ВСЕ поля
fetch('/api/v1/users/123', {
method: 'PUT',
body: JSON.stringify({
id: 123,
name: 'John',
email: 'john@example.com',
age: 30,
city: 'New York'
// Все поля обязательны
})
});
// PATCH - частичное обновление
// Только измененные поля
fetch('/api/v1/users/123', {
method: 'PATCH',
body: JSON.stringify({
name: 'Jane' // Только это изменилось
})
});
Передача файлов (multipart/form-data)
Для файлов используй FormData, а не JSON:
const updateUserWithAvatar = async (userId, formData) => {
const response = await fetch(`/api/v1/users/${userId}`, {
method: 'PUT',
// НЕ устанавливай Content-Type для FormData
// браузер сам установит multipart/form-data
body: formData // FormData с файлом
});
return response.json();
};
// Использование
const handleUpload = (userId, file) => {
const formData = new FormData();
formData.append('name', 'John Doe');
formData.append('email', 'john@example.com');
formData.append('avatar', file);
updateUserWithAvatar(userId, formData);
};
Как получить текущие данные перед PUT
Обычно нужно получить ресурс, изменить его, и отправить обратно:
async function updateUserName(userId, newName) {
// 1. Получить текущие данные
const getResponse = await fetch(`/api/v1/users/${userId}`);
const user = await getResponse.json();
// 2. Изменить только нужное поле
user.name = newName;
// 3. Отправить полный объект
const putResponse = await fetch(`/api/v1/users/${userId}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(user)
});
return putResponse.json();
}
// Лучше - использовать PATCH если поддерживается
async function updateUserName(userId, newName) {
const response = await fetch(`/api/v1/users/${userId}`, {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: newName })
});
return response.json();
}
Конвенции для ID ресурса
// ID в URL, а не в body
// Правильно
fetch('/api/v1/users/123', {
method: 'PUT',
body: JSON.stringify({
name: 'John',
email: 'john@example.com'
// ID не повторяем
})
});
// Иногда ID повторяют для верификации
fetch('/api/v1/users/123', {
method: 'PUT',
body: JSON.stringify({
id: 123, // Опционально, для проверки
name: 'John',
email: 'john@example.com'
})
});
Обработка ошибок
async function updateUser(userId, userData) {
try {
const response = await fetch(`/api/v1/users/${userId}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(userData)
});
// 200 OK - успех
if (response.status === 200) {
return await response.json();
}
// 400 Bad Request - невалидные данные
if (response.status === 400) {
const error = await response.json();
throw new Error(error.message);
}
// 404 Not Found - ресурс не существует
if (response.status === 404) {
throw new Error('User not found');
}
// 409 Conflict - конфликт версии (optimistic lock)
if (response.status === 409) {
throw new Error('Resource was modified by someone else');
}
throw new Error('Unknown error');
} catch (error) {
console.error('Update failed:', error);
throw error;
}
}
Оптимистичное обновление
function useUpdateUser(userId) {
const [user, setUser] = useState(null);
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState(null);
const updateUser = async (updates) => {
// Сохраняем старые данные
const previousUser = user;
// Оптимистично обновляем UI
setUser({ ...user, ...updates });
setIsLoading(true);
try {
const response = await fetch(`/api/v1/users/${userId}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ ...user, ...updates })
});
if (!response.ok) throw new Error('Update failed');
const updatedUser = await response.json();
setUser(updatedUser); // Синхронизируем с сервером
} catch (err) {
// При ошибке откатываем
setUser(previousUser);
setError(err.message);
} finally {
setIsLoading(false);
}
};
return { user, isLoading, error, updateUser };
}
Типизация в TypeScript
interface User {
id: number;
name: string;
email: string;
phone?: string;
}
async function updateUser(
userId: number,
userData: Partial<User>
): Promise<User> {
const response = await fetch(`/api/v1/users/${userId}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(userData)
});
if (!response.ok) throw new Error('Update failed');
return response.json();
}
// Использование
await updateUser(123, {
name: 'John',
email: 'john@example.com'
});
Best Practices
- JSON Body: Данные в body как JSON, ID в URL
- Все поля: PUT требует передачи всех полей
- Content-Type: Установи 'application/json'
- Обработка ошибок: Проверяй все статус коды
- Оптимистичные обновления: Обновляй UI до ответа сервера
- Версионирование: Рассмотри использование version/etag для конфликтов
- Валидация: Валидируй данные на клиенте до отправки
- Документация: Четко определи какие поля обязательны