Разберите ошибку: частичное изменение профиля обрабатывается как полная замена ресурса. Чем это опасно?
Если запрос содержит только изменяемые поля, а сервер трактует его как полную замену ресурса, все отсутствующие поля могут быть удалены, сброшены или заменены значениями по умолчанию. Это приводит к потере данных и нарушает ожидания клиента.
Контракт должен явно разделять полную замену и частичное изменение: для полной замены передаётся целое представление ресурса, а для частичного изменения используется операция с определённой семантикой, например PATCH.
Проблема возникла из-за того, что API работают с ресурсами, но клиенту часто требуется изменить только один их атрибут. Передача всей сущности для каждого небольшого изменения увеличивает размер запроса, усложняет клиентскую логику и повышает риск случайно перезаписать актуальные данные.
В HTTP различаются семантики PUT и PATCH. PUT обычно описывает замену состояния ресурса переданным представлением, а PATCH предназначен для частичного изменения, однако точные правила применения PATCH определяются форматом документа изменений и контрактом API.
Допустим, профиль содержит имя, телефон, адрес и настройки уведомлений. Клиент хочет изменить только телефон и отправляет два поля, но сервер воспринимает их как полное состояние профиля.
Если сервер обнуляет отсутствующие поля, пользователь потеряет адрес и настройки. Если он подставляет значения по умолчанию, данные могут измениться незаметно; особенно опасно, когда клиент использует устаревшую модель ресурса и не знает о новых полях, появившихся в системе.
Сначала нужно зафиксировать семантику операции в API-контракте. Полная замена требует передавать полный набор управляемых полей и явно описывать, что происходит с отсутствующими атрибутами. Частичное изменение должно изменять только указанные поля, оставляя остальные без изменений.
Важно различать «поле не передано» и «поле передано со значением null». В первом случае клиент обычно не просит менять атрибут, во втором — может явно просить очистить его. Если API не поддерживает такую разницу, это ограничение должно быть документировано.
У PATCH нет единственного формата содержимого. Один контракт может описывать изменения как набор операций над путями, другой — как частичный объект с правилами слияния. Поэтому недостаточно назвать метод PATCH: необходимо определить формат, поведение для неизвестных полей, правила валидации и допустимость очистки значений.
Для вложенных объектов и массивов нужны отдельные правила. Например, замена всего массива и добавление одного элемента — разные операции; неясная семантика может привести к удалению элементов, которые клиент не видел.
Частичное изменение также должно проходить обычные проверки авторизации, бизнес-ограничений и согласованности. Если несколько клиентов изменяют один ресурс, частичная семантика сама по себе не решает конфликт версий: для этого отдельно применяются механизмы контроля актуальности данных, например версия ресурса или условное обновление.
Основной компромисс таков: полная замена проще для понимания и валидации, но требует полного представления ресурса; частичное изменение экономит трафик и снижает объём данных в запросе, но требует более точного контракта и сложнее обрабатывается на сервере.
В системе управления сотрудниками старый клиент отправлял при редактировании только изменённые поля. Новый сервер реализовал операцию как полную замену и при сохранении подставлял значения по умолчанию для отсутствующих атрибутов. После обновления клиенты начали сбрасывать настройки уведомлений сотрудников.
Рассматривались три варианта. Можно было заставить клиентов всегда читать ресурс перед изменением и отправлять полную модель, но это увеличивало число запросов и сохраняло риск работы с устаревшими данными. Можно было сделать серверное слияние любого входящего объекта с текущим ресурсом, но тогда становилось неясно, как очистить поле и как трактовать вложенные объекты. Третий вариант — формально разделить операции полной замены и частичного изменения.
Выбрали третий вариант: для частичных изменений зафиксировали отдельный формат PATCH, явно различили отсутствующие поля и null, добавили проверки неизвестных атрибутов и тесты на сохранение не затронутых полей. Полная замена осталась отдельной операцией с обязательной передачей полного представления. В результате старые клиенты не могли незаметно очищать новые поля, а правила обновления стали проверяемыми по контракту.
Нет. PATCH только указывает на намерение частично изменить ресурс, но конкретный результат зависит от формата документа изменений и его правил. Некорректно реализованный PATCH может заменить вложенный объект целиком, удалить поля или выполнить операцию, которую клиент не ожидал. В контракте нужно описать допустимые изменения и их последствия.
Отсутствие поля обычно означает «не изменять текущее значение», а null может означать «очистить значение». Если сервер их не различает, клиент не сможет надёжно выразить намерение очистить атрибут или сохранить его без изменений. Это особенно важно для необязательных полей, где null имеет бизнес-смысл.
Оно ограничивает набор полей, которые операция пытается изменить, но не проверяет актуальность прочитанного состояния. Два клиента могут изменить один и тот же атрибут последовательно, и последний запрос всё равно затрёт результат первого. Для защиты нужен отдельный механизм обнаружения конфликта, например проверка версии ресурса перед применением изменения.