
Комментарии 35
При желании нет никаких проблем сделать PATCH идемпотентным, нужно лишь подобрать соответствующий формат дельты.
Почему же?
Если говорить про json api, и оставить за скобками массивы (там и правда есть сложности), то достаточно выкинуть из схемы все неизменяемые и вычисляемые поля, а остальные сделать необязательными.
Да, подобная схема не позволит выражать изменения вида "удалил одну букву из значения некоторого свойства", но я не представляю кому вообще нужны такие подробные изменения.
DELETE идемпотентен
Какой эгоцентризм. Вы же не в транзакции работаете. Сосед мог напостить нового между двумя делитами.
Результат окажется один и тот же независимо от того, постил сосед между делитами или нет. В этом и суть идемпотентного удаления.
Тогда всю статью переписывать надо. Потому что там утверждается, что второе применение не вызывает изменений.
Не вызывает изменений если идёт сразу после первого.
Выражаясь языком математики, f(f(x)) = f(x)
Я-то своё определение взял из учебника математики, а вы свою чушь откуда взяли?
Определение в вебе соответствует математическому.
Поздравляю, по вашему определению и любой PATCH идемпотентен.
Неа, не любой. К примеру, стандартный diff для текстовых файлов неидемпотентен.
Про математику — надо смотреть как минимум определение на афинных полугруппах (потому что непрерывных параметров в вебе не бывает, печаль).
А где в формуле f(f(x)) = f(x) хотя бы намёк на непрерывный параметр?
PATCH в вебе — описан в RFC 5789
Формат патча в RFC 5789 не описан, он определяется реализацией (а также Content-Type), и может быть любым. В том числе тем, что используется в утилитах diff и patch.
У стандартного diff, как минимум, два аргумента, он вообще из другой оперы.
Очевидно, я говорил не об утилите, а о формате, в котором она выводит различия. Если бы вы сосредоточились на попытках меня понять, а не оскорбить -= вы бы это поняли.
Про методы GET, POST, PUT, DELETE довольно странные мысли. Любой из методов можно сделать идемпотентным или не идемпотентным. Метод - это лишь вариант интерфейса.
Не так давно я работал с API банка, у которого вообще все было сделано через GET, причем параметры через url. И добавление заказов, и удаление, и запрос статуса... все-все-все :)
Видимо, да. Но об этом остается только догадываться. Если бы я не был немного в теме, я бы не понял того, что речь о спецификации этих методов. Этого не понятно ни по названию, ни по содержанию статьи. Ну и не помешало бы разъяснение, что это лишь рекомендации, которые соблюдаются далеко не всегда. Я бы даже сказал, что редко соблюдаются...
Важная часть, которую надо бы вынести большими буквами для новичков в начало статьи - методы PUT и DELETE ДОЛЖНЫ быть идемпотентный в соответствии с RFC, но в реальной жизни всегда проверяйте, что нашкодили программисты, потому что никто этого не гарантирует. По сути это все го лишь слова для разграничения разных методов, а что они делают зависит от кода, и GET с POST могут использоваться для удаления записей.
И, кстати, да, по RFC идемпотентными являются в явном виде только PUT и DELETE, GET не обязан быть таковым.
Метод без побочных эффектов всегда идемпотентен.
Применение патча формата diff к файлу, очевидно, меняет этот самый файл. Это и есть побочный эффект с точки зрения стандарта HTTP.
А что не так-то? Основной результат запроса - это ответ сервера. Всё остальное - побочные эффекты.
И, если что, "метод без побочных эффектов всегда идемпотентен" написано прямо в стандарте:
A sequence that never has side effects is idempotent, by definition
-- https://datatracker.ietf.org/doc/html/rfc2616#section-9.1.2
(одиночный запрос является тривиальной разновидностью последовательности запросов, если вы решите ещё и тут придраться)
А правильный ответ: идемпотентность зависит от того какими кривыми руками спроектировано АПИ. Я так понимаю тут речь про канонический REST. В реале довольно часто встречается следующее: [POST] "/object-type/:id" для обновления объекта, а вот если вызвать без параметра в урле, то будет создан новый объект.
Формально по HTTP-спецификации POST не считается идемпотентным, но на уровне приложения его вполне можно (и часто нужно) таковым делать. Классический пример — приём уведомлений от банка или платёжной шлюзы об успешной оплате.
При первом запросе может произойти таймаут или потеря ответа на сетевом уровне. Банк, не получив подтверждения, автоматически повторит запрос. Если на дубликат вы будете отвечать ошибкой вроде «заказ уже оплачен», клиент воспримет это как неуспешную попытку и продолжит ретраи. Это создаёт бессмысленную нагрузку и ломает ожидаемую семантику повторных вызовов.
Правильное поведение — возвращать тот же успешный ответ (200 OK или 201 Created с исходным payload), что и при первом успешном вызове, даже если состояние системы уже не меняется. В продакшене для этого используют явные ключи идемпотентности (Idempotency-Key): первый запрос выполняется, ответ кешируется, все последующие запросы с тем же ключом просто отдают закешированный результат без повторного выполнения бизнес-логики.
Для финансовых API и вебхуков это уже де-факто стандарт: идемпотентность обеспечивает не HTTP-метод, а слой дедупликации на стороне приложения. Так вы гарантируете стабильность интеграции и избавляетесь от бесконечных ретраев.
Идемпотентность: искусство не менять мир дважды