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

В чем разница между PUT и PATCH?

2.0 Middle🔥 222 комментариев
#Сетевые протоколы и API

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

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

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

Разница между HTTP-методами PUT и PATCH

PUT и PATCH — это два HTTP-метода, используемые для обновления ресурсов на сервере, но они принципиально отличаются по семантике и способу применения. В контексте RESTful API понимание их различий критически важно для построения корректных и предсказуемых интерфейсов.

Ключевая семантика: Замена vs. Частичное обновление

PUT является идемпотентным методом, предназначенным для полной замены ресурса. Клиент отправляет на сервер представление ресурса, которое должно заменить существующее. Если ресурс ранее не существовал, он может быть создан (хотя эту роль чаще отводят методу POST).

// Пример PUT-запроса в Go для полного обновления пользователя
type User struct {
    ID   int    `json:"id"`
    Name string `json:"name"`
    Age  int    `json:"age"`
}

func updateUserHandler(w http.ResponseWriter, r *http.Request) {
    if r.Method != http.MethodPut {
        http.Error(w, "Метод не поддерживается", http.StatusMethodNotAllowed)
        return
    }

    var userUpdates User
    if err := json.NewDecoder(r.Body).Decode(&userUpdates); err != nil {
        http.Error(w, "Неверный формат данных", http.StatusBadRequest)
        return
    }
    // ВАЖНО: Ожидается, что в userUpdates будут ВСЕ поля пользователя.
    // Отсутствующие поля могут быть интерпретированы как нулевые значения.
    // Происходит ПОЛНАЯ ЗАМЕНА ресурса.
    saveUserToDB(userUpdates) // Записываем объект целиком
    w.WriteHeader(http.StatusOK)
    json.NewEncoder(w).Encode(userUpdates)
}

PATCH, в отличие от PUT, предназначен для частичного обновления ресурса. Клиент отправляет только те изменения, которые необходимо применить, а не полное представление. PATCH не является идемпотентным по своей природе (хотя на практике его можно реализовать идемпотентно).

// Пример PATCH-запроса в Go для частичного обновления
type UserPatch struct {
    Name *string `json:"name,omitempty"` // Указатели позволяют отличать отсутствие поля от нулевого значения
    Age  *int    `json:"age,omitempty"`
}

func patchUserHandler(w http.ResponseWriter, r *http.Request) {
    if r.Method != http.MethodPatch {
        http.Error(w, "Метод не поддерживается", http.StatusMethodNotAllowed)
        return
    }

    var patch UserPatch
    if err := json.NewDecoder(r.Body).Decode(&patch); err != nil {
        http.Error(w, "Неверный формат данных", http.StatusBadRequest)
        return
    }

    currentUser := getUserFromDB()
    // Применяем только переданные в запросе поля
    if patch.Name != nil {
        currentUser.Name = *patch.Name
    }
    if patch.Age != nil {
        currentUser.Age = *patch.Age
    }
    saveUserToDB(currentUser)
    w.WriteHeader(http.StatusOK)
    json.NewEncoder(w).Encode(currentUser)
}

Сравнительная таблица

КритерийPUTPATCH
Основное назначениеПолная замена ресурсаЧастичное обновление ресурса
ИдемпотентностьДа (повторный вызов даёт тот же результат)Нет (в общем случае, зависит от реализации)
Тело запросаПолное представление нового состояния ресурсаНабор изменений (дельта, patch-документ)
Создание ресурсаМожет (если ресурс не существует)Только для существующих ресурсов (обычно)
Размер запросаКак правило, больше (передаются все поля)Как правило, меньше (только изменяемые поля)

Форматы для PATCH

Для PATCH важно определить формат передаваемых изменений. Наиболее распространёнными являются:

  • JSON Merge Patch (RFC 7396): Простой формат, где в теле передаётся JSON-объект с изменяемыми полями. Не поддерживает явные операции вроде установки null или работы с массивами по индексам.
  • JSON Patch (RFC 6902): Более мощный и стандартизированный формат, использующий массив операций (add, remove, replace, move, copy, test).
// Пример JSON Patch запроса [{"op": "replace", "path": "/name", "value": "НовоеИмя"}]
type JSONPatchOperation struct {
    Op    string      `json:"op"`
    Path  string      `json:"path"`
    Value interface{} `json:"value,omitempty"`
}

func jsonPatchHandler(w http.ResponseWriter, r *http.Request) {
    var patches []JSONPatchOperation
    if err := json.NewDecoder(r.Body).Decode(&patches); err != nil {
        http.Error(w, "Неверный JSON Patch", http.StatusBadRequest)
        return
    }
    // Применение каждой операции из массива patches к ресурсу
}

Практические рекомендации для Go-разработчика

  • Используйте PUT, когда клиент может и должен отправить полное представление ресурса. Это упрощает валидацию и логику на сервере.
  • Выбирайте PATCH, когда необходимо обновить одно-два поля большого объекта или когда передача всего объекта неэффективна по трафику.
  • Всегда проверяйте существование ресурса при обработке PUT-запроса, если не хотите разрешать неявное создание.
  • Для PATCH-метода строго документируйте поддерживаемый формат изменений (например, JSON Merge Patch или JSON Patch).
  • Обеспечьте атомарность PATCH-операций. Все изменения в одном запросе должны применяться либо все, либо ни одно (например, в рамках транзакции БД).
  • Будьте осторожны с конкурентным доступом. И для PUT, и для PATCH актуальны проблемы Lost Update. Рассмотрите использование ETag и заголовка If-Match для реализации оптимистичной блокировки.

В Go-экосистеме при построении REST API с использованием популярных фреймворков (Gin, Echo, Chi) выбор между PUT и PATCH ложится на разработчика, так как эти фреймворки предоставляют роутинг для любых HTTP-методов. Ответственность за корректную обработку семантики каждого метода и выбор форматов данных (особенно для PATCH) лежит на команде разработки. Правильное применение этих методов повышает предсказуемость, эффективность и надёжность вашего API.

В чем разница между PUT и PATCH? | PrepBro