Почему метод PATCH не идемпотентен?

48

Мне было интересно об этом.

Предположим , у меня есть userресурс с idи nameполей. Если я хочу обновить поле, я мог бы просто сделать запрос PATCH к ресурсу, как это

PATCH /users/42
{"name": "john doe"} 

И тогда приложение обновит имя пользователя 42.

Но почему, если я повторю этот запрос, результат будет другим?

Согласно RFC 5789

PATCH не является ни безопасным, ни идемпотентным

mattecapu
источник
Что делать, если между вызовами кто-то еще делает запрос на обновление пользователя 42{"name": "bendjamin franklin"}
gnat
@gnat не имеет ли аналогичный аргумент и метод PUT, который вместо этого считается идемпотентным? (см. goo.gl/t24ZSJ )
mattecapu
«PUT имеет идемпотентную семантику и, следовательно, может безопасно использоваться для абсолютных обновлений (то есть мы отправляем все состояние ресурса на сервер), но не для относительных обновлений (то есть мы отправляем только изменения в состояние ресурса) , поскольку это нарушает его семантика ... "( запросы POST и PUT - это просто соглашение? )
gnat
1
Очевидно ... Но вы могли бы сказать, что PUT не идемпотентен, потому что между двумя равными запросами второй клиент может сделать третий запрос между ними. Но так как нам нет дела до предыдущих данных, это не проблема. Тот же аргумент верен для запросов PATCH.
Mattecapu
2
Я позволил себе добавить ссылку на соответствующую спецификацию, так как считаю, что она весьма актуальна в контексте этого вопроса.
Пит

Ответы:

34

Запрос PATCH может быть идемпотентным, но это не обязательно. Вот почему он характеризуется как неидемпотентный.

Может ли PATCH быть идемпотентным или нет, сильно зависит от того, как сообщаются требуемые изменения.
Например, если формат патча имеет форму {change: 'Name' from: 'benjamin franklin' to: 'john doe'}, то любой запрос PATCH после первого будет иметь другой эффект (ответ об ошибке), чем первый запрос.
Другой причиной неидемпотентности может быть то, что применение модификации к чему-то другому, чем исходный ресурс, может сделать ресурс недействительным. Это также будет иметь место, если вы примените изменение несколько раз.

Барт ван Инген Шенау
источник
3
Я не понимаю последний абзац. Можете ли вы привести пример того, как «применение модификации к чему-то другому, кроме исходного ресурса, может сделать ресурс недействительным», и как это связано с многократным применением изменения к одному и тому же ресурсу?
Робин Грин,
3
@RobinGreen: Предположим, что ресурс представлен в XML с требованием, чтобы было не более одного <name>элемента. Если PATCH добавляет <name>элемент к ресурсу, который изначально не содержал его, то применение PATCH дважды (или применение его к ресурсу, который уже содержит a <name>) делает ресурс недействительным, поскольку он внезапно будет содержать два <name>элемента, что недопустимо за такие ресурсы.
Барт ван Инген Шенау
13
Первый приведенный вами пример не имеет отношения к ИМХО, поскольку он идемпотентен. Пример с DELETE, который является идемпотентом, первый запрос: ресурс существует, но был удален (возвращает 2xx), второй запрос: ресурс не существует и все еще не существует после запроса, возвращает 4xx. Состояние сервера не изменилось, следовательно, идемпотентность. В вашем примере первый запрос: PATCH из BenFra в JohDoe, имя ресурса было BenFra, но теперь это JohDoe (возвращает 2xx), второй запрос: PATCH из BenFra в JohDoe, имя ресурса было JohDoe и по-прежнему JohDoe (возвращает 4xx). Так что это не говорит о том, что PATCH может быть неидемпотентным.
SP00m
Более подробная информация здесь: stackoverflow.com/q/4088350/1225328
sp00m
8

Я думаю, что четкий ответ, когда PATCH в не идемпотентном является этот абзац из RFC 5789:

Существуют также случаи, когда форматы исправлений не должны работать с известной базовой точки (например, добавление текстовых строк в файлы журналов или не сталкивающихся строк в таблицы базы данных), и в этом случае такая же забота в клиентских запросах не требуется. ,

Поскольку RFC указывает, что исправление содержит некоторые «общие изменения» ресурса, мы должны смотреть не только на типичную замену поля. Если ресурс предназначен для счетчика, то patch может запросить его приращение, что явно не идемпотет.

Иван
источник
2

PATCHЗапросы описывают набор операций, которые будут применены к ресурсу. Если вы примените один и тот же набор операций дважды к одному и тому же ресурсу, результат может не совпадать. Это потому, что определение операций зависит от вас. Другими словами, вы должны определить правила объединения .

Помните, что PATCHзапрос может использоваться для исправления ресурсов во многих различных форматах, а не только в формате JSON.

Таким образом, PATCHзапрос может быть идемпотентным, если вы определяете правила слияния как идемпотентные .

Идемпотентный пример:

// Original resource
{
  name: 'Tito',
  age: 32
}

// PATCH request
{
  age: 33
}

// New resource
{
  name: 'Tito',
  age: 33
}

Неидемпотентный пример:

// Original resource
{
  name: 'Tito',
  age: 32
}

// PATCH request
{
  $increment: 'age'
}

// New resource
{
  name: 'Tito',
  age: 33
}

Во втором примере я использовал синтаксис типа «Монго», который я создал для увеличения атрибута. Понятно, что это не идемпотент, так как отправка одного и того же запроса несколько раз приведет к разным результатам каждый раз.

Теперь вы можете задаться вопросом, допустимо ли использование такого составного синтаксиса. Согласно стандартам это:

Разница между запросами PUT и PATCH отражается в способе, которым сервер обрабатывает вложенный объект для изменения ресурса, идентифицируемого Request-URI. В запросе PUT вложенный объект считается модифицированной версией ресурса, хранящегося на исходном сервере, и клиент запрашивает замену сохраненной версии. Однако с помощью PATCH вложенный объект содержит набор инструкций, описывающих, как ресурс, находящийся в данный момент на исходном сервере, должен быть модифицирован для создания новой версии.

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

JBM
источник