Содержимое
Мы всё неправильно понимаем про HTTP метод PATCH. Иногда читать стандарты очень увлекательно — становится понятно, что люди имели в виду на самом деле. Читать книги и статьи опаснее — там уже личные интерпретации, кто как понял. Почти во всех примерах, что мне встречались, тело запроса PATCH описано, как набор новых значений полей в JSON — с оговоркой, что в случае PUT мы должны были бы передать все поля, а не только изменившиеся, а в случае PATCH — только изменившиеся. Но в стандарте — RFC 5789 — написано совсем другое! В запросе PATCH должен передаваться набор изменений, применяемых к ресурсу, в формате "патч-документа". Что это за формат — зависит от медиа-типа ресурса. В реестре медиа-типов IANA есть, например, типы патч-документов для json: application/json-patch+json и application/merge-patch+json; и тип для XML: xml-patch+xml (ну ещё несколько специфических). Документ JSON-patch [RFC 6902] выглядит именно как набор операций, вносящих изменения в JSON: [ { "op": "test", "path": "/a/b/c", "value": "foo" }, { "op": "remove", "path": "/a/b/c" }, { "op": "add", "path": "/a/b/c", "value": [ "foo", "bar" ] }, { "op": "replace", "path": "/a/b/c", "value": 42 }, { "op": "move", "from": "/a/b/c", "path": "/a/b/d" }, { "op": "copy", "from": "/a/b/d", "path": "/a/b/e" } ] op — это, понятно, название операции, вот какие операции могут быть: add — добавить элемент в массив или свойство в объект; remove — удалить свойство объекта или элемент массива; replace — соответственно, заменить (как последовательное применения remove и add); move — переместить значение из одного места (пути) в другой (тоже как remove и add, но у add будет другой путь); copy — скопировать значение и вставить в другое место; test — проверить значение по указанному адресу на соответствие (без масок или регулярных выражений, просто на совпадение). Понятно, что неидемпотентным PATCH делает именно операция add — если будем применять её несколько раз к массиву, добавится много элементов вместо одного. То, как обычно описывают формат применения PATCH — больше похоже на merge-patch+json: там мы действительно передаем только изменившиеся свойства, и null для тех свойств, которые хотим удалить. В любом случае, тип медиа не должен быть просто application/json — по стандарту (а точнее — по errata к стандарту, списку исправлений), сервер вообще должен возвращать ошибку 415, если в запросе PATCH приходит тот же медиа тип, что у ресурса — то есть, не json-patch, просто json. Это чтобы избежать неоднозначности интерпретации. А в каких форматах сервер готов принимать патч-документ — он должен сам сказать в ответе на запрос OPTIONS: Accept-Patch: application/prs.example.patch+json, application/json-patch+json, application/merge-patch+json. Вот так PATCH должен работать на самом деле. Ясно, что он так практически никогда не работает, так что можете этими знаниями блеснуть на собеседовании, разве что. И то — будьте осторожны, а то интервьюер примет вас за зануду-теоретика, выучившего стандарты, но далекого от практики. А на практике ни один фронтендер не будет вам конструировать сложный патч-документ, когда можно передать просто набор изменившихся полей. Ну, или пока дело не дойдет до какого-то очень сложного по структуре, со многими уровнями вложенности объекта.