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

Ответ

Основное различие между PUT и PATCH заключается в способе обновления ресурса на сервере.

  • PUT — это идемпотентный метод, который используется для полной замены ресурса. Вы должны отправить в теле запроса все поля ресурса. Если ресурс не существует, PUT может его создать.

  • PATCH — это неидемпотентный метод, предназначенный для частичного обновления ресурса. Вы отправляете только те поля, которые хотите изменить, а остальные остаются нетронутыми.

Пример на Go для PATCH

Для корректной обработки частичных обновлений в Go часто используют указатели в структуре. Это позволяет отличить поле, которое не было передано (nil), от поля, которому было передано нулевое значение (например, пустая строка "").

// Структура для частичного обновления пользователя
type UpdateUserPayload struct {
    Name  *string `json:"name,omitempty"`
    Email *string `json:"email,omitempty"`
}

// currentUser - существующий ресурс, который мы обновляем
var currentUser = User{ID: 1, Name: "Admin", Email: "admin@example.com"}

func updateUser(w http.ResponseWriter, r *http.Request) {
    if r.Method != http.MethodPatch {
        http.Error(w, "Метод не разрешен", http.StatusMethodNotAllowed)
        return
    }

    var payload UpdateUserPayload
    if err := json.NewDecoder(r.Body).Decode(&payload); err != nil {
        http.Error(w, err.Error(), http.StatusBadRequest)
        return
    }

    // Применяем обновления, только если поля были переданы в JSON
    if payload.Name != nil {
        currentUser.Name = *payload.Name
    }
    if payload.Email != nil {
        currentUser.Email = *payload.Email
    }

    // ... логика сохранения в БД ...

    w.WriteHeader(http.StatusOK)
    json.NewEncoder(w).Encode(currentUser)
}

Ключевые моменты:

  • Идемпотентность: PUT идемпотентен (повторные запросы с одинаковыми данными дают тот же результат), а PATCH — нет (два одинаковых PATCH запроса могут привести к разным результатам, если они, например, инкрементируют значение).
  • Пропускная способность: PATCH экономит трафик, так как передает только измененные данные.
  • Форматы: Для PATCH часто используют Content-Type: application/merge-patch+json (для простого слияния) или application/json-patch+json (для более сложных операций, описанных в RFC 6902).

Ответ 18+ 🔞

Да ты посмотри, какая интересная хуйня вырисовывается! Эти два метода, PUT и PATCH, — они вроде как близнецы, но один, блядь, полный максималист, а второй — хитрая жопа, которая под шумок своё делает.

Вот смотри, PUT — это такой мужик, который заходит в квартиру и говорит: «Всё, блядь, старую обстановку нахуй, сейчас я всё по-новому расставлю!». Он требует, чтобы ты ему отправил всё поля ресурса, даже если ты поменял только имя. Не прислал что-то? Он это обнулит, ёпта! Зато если ресурса нет — он его создаст, такой вот хозяйственный. И главное, идемпотентный, то есть ты можешь десять раз подряд один и тот же PUT отправить — и хуй с ним, ничего страшного, результат будет одинаковый.

А PATCH — это, сука, мартышлюшка подкралась. Он не хочет всё ломать. Он такой: «Ой, а можно я только диван передвину и картину перевешу? Остальное пусть как было». И ты отправляешь только то, что хочешь поменять. Остальное он не трогает, умный такой. Но с ним надо осторожно — он не идемпотентный. Если два раза подряд отправить PATCH, который, например, счётчик увеличивает, то он его и увеличит два раза, вот такая подстава.

А теперь, блядь, смотри на этот код на Go. Тут вся соль в том, чтобы отличить, когда тебе в запросе прислали пустое значение, а когда про поле вообще забыли. Для этого используют указатели, гениальная, блядь, мысль!

type UpdateUserPayload struct {
    Name  *string `json:"name,omitempty"`
    Email *string `json:"email,omitempty"`
}

Видишь эти звёздочки? Если в JSON пришло поле "name": "" — указатель будет не nil, а указывать на пустую строку. Значит, юзер хочет имя очистить. А если поля name вообще не было в JSON — указатель будет nil. И тогда мы это поле не трогаем, оставляем как есть. Хитрая, блядь, логика, но правильная!

if payload.Name != nil {
    currentUser.Name = *payload.Name // Меняем только если было передано
}

Вот так вот, одним if отсекаешь все нежелательные изменения. Красота, в рот меня чих-пых!

Так что запомни: хочешь всё переписать нахуй — бери PUT. Хочешь точечно подпилить, как хирург-извращенец — твой выбор PATCH. Главное — не перепутай, а то вместо смены пароля пользователю всю его анкету, сука, обнулишь.