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

Как принято передавать данные в 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

  1. JSON Body: Данные в body как JSON, ID в URL
  2. Все поля: PUT требует передачи всех полей
  3. Content-Type: Установи 'application/json'
  4. Обработка ошибок: Проверяй все статус коды
  5. Оптимистичные обновления: Обновляй UI до ответа сервера
  6. Версионирование: Рассмотри использование version/etag для конфликтов
  7. Валидация: Валидируй данные на клиенте до отправки
  8. Документация: Четко определи какие поля обязательны