REST — изменить часть ресурса — PUT или POST

Я вижу много маханий руками на тему того, как обновить только часть ресурса (например, индикатор состояния) с помощью REST.

Варианты вроде такие:

  1. Пожаловаться, что HTTP не имеет команды PATCH или MODIFY. Однако принятый ответ на HTTP MODIFY verb для REST? хорошо показывает почему это не такая хорошая идея, как может показаться.

  2. Используйте POST с параметрами и укажите метод (например, параметр с именем «действие»). Некоторые предложения заключаются в том, чтобы указать заголовок X-HTTP-Method-Override с самоопределяемым именем метода. Это, кажется, приводит к уродству переключения внутри реализации в зависимости от того, что вы пытаетесь сделать, и быть открытым для критики за то, что это не особенно RESTful способ использования POST. На самом деле такой подход начинает ощущаться как интерфейс типа RPC.

  3. Используйте PUT, чтобы перезаписать вложенный ресурс ресурса, который представляет определенные атрибуты для обновления. На самом деле, это фактически перезапись подресурса, что соответствует духу PUT.

На данный момент я вижу вариант 3 как наиболее разумный вариант.

Это лучшая практика или анти-шаблон? Есть ли другие варианты?


person Jacob Zwiers    schedule 05.02.2010    source источник


Ответы (6)


Вариант 3 (PUT для какого-то отдельного подресурса) — ваш лучший выбор прямо сейчас, и не обязательно было бы «неправильным» просто использовать POST на самом основном ресурсе — хотя вы можете не согласиться с этим в зависимости от того, насколько педантичен вы хотите быть об этом.

Придерживайтесь 3 и используйте более детализированные подресурсы, и если вам действительно нужно поведение, подобное PATCH, используйте POST. Лично я по-прежнему буду использовать этот подход, даже если PATCH действительно окажется жизнеспособным вариантом.

person Mike    schedule 05.02.2010

Есть два способа просмотреть обновление статуса.

  1. Обновление до вещи. Это ПУТ. Вариант 3

  2. Добавление дополнительной записи журнала в историю вещи. Элемент списка в этой последовательности записей журнала является текущим состоянием. Это ПОСТ. Вариант 2.

Если вы занимаетесь хранением данных или функциональным программированием, вы, как правило, с недоверием относитесь к изменениям статуса и предпочитаете размещать новый фрагмент исторического факта в статичной, неизменной вещи. Это требует отделения вещи от истории вещи; ведет к двум таблицам.

В противном случае вы не возражаете против «обновления», чтобы изменить статус вещи, и вы довольны PUT. Это не делает различий между вещью и ее историей и сохраняет все в одной таблице.

Лично я обнаруживаю, что я все меньше и меньше доверяю изменяемым объектам и PUT (за исключением «исправления ошибок»). (И даже тогда, я думаю, что старое можно оставить на месте, а новое добавить со ссылкой на предыдущую версию самого себя.)

Если есть изменение статуса, я думаю, что должен быть журнал состояния или история, и должен быть POST, чтобы добавить новую запись в эту историю. Может быть некоторая оптимизация для отражения «текущего» состояния объекта, к которому это относится, но это только закулисная оптимизация.

person S.Lott    schedule 05.02.2010
comment
Спасибо за ответ. Я должен был быть более избирательным в отношении примера, который я использовал (например, обновление статуса). Проблема, которую я решаю, более общая, чем просто статусы. И там, где это статус, который я обновляю, это действительно обновление, чтобы показать, что принимающая система находится в процессе работы над ресурсом (например, подтверждение). Я включу проверку ошибок, чтобы определить допустимые переходы состояний для поля. Итак, на самом деле моя ситуация соответствует вашему № 1 (который является исходным № 3). - person Jacob Zwiers; 05.02.2010
comment
Если ваша модель данных не имеет истории, то обновление является распространенным запасным планом. Однако, если вы все еще строите, подумайте о том, чтобы сохранить всю историю. В качестве оптимизации дублирование самой последней истории в самой вещи. - person S.Lott; 05.02.2010
comment
Отличный ответ. Вопрос: Считаете ли вы неправильным, чтобы PUT обновляло состояние ресурса и вызывало появление в журнале записи о его изменении? Поскольку вызовы GET могут создавать и создают записи в журнале, и это было оправдано тем, что ведение журнала является внутренней проблемой, не будет ли уместным ведение журнала PUT? Это позволило бы сохранить идемпотентную природу PUT, за исключением случаев, когда это состояние было изменено вызовом API от другого клиента, и поэтому для этого подходит ведение журнала. Нет? - person MikeSchinkel; 22.06.2016

HTTP имеет команду PATCH. Он определен в разделе 19.6.1.1 RFC 2068 и был обновлен. в http://tools.ietf.org/html/draft-dusseault-http-patch-16, в настоящее время ожидает публикация в виде RFC.

person Julian Reschke    schedule 05.02.2010
comment
На практике, вероятно, лучше пока использовать POST или просто разделить ваш ресурс на подресурсы и PUTing для них. - person Mike; 05.02.2010
comment
Является ли отсутствие инструментов проблемой, безусловно, зависит от инструментов, которые у вас есть, верно? Поэтому я бы порекомендовал попробовать, а не отбрасывать вариант заранее. - person Julian Reschke; 06.02.2010
comment
PATCH решает крошечную проблему, которую уже можно было решить с помощью POST и некоторого здравого смысла — им почти наверняка будут злоупотреблять и наносить ущерб сети (и любой другой системе, в которую вы его допускаете), поэтому я бы просто избегал этого, оно того не стоит. . - person Mike; 06.02.2010
comment
Повредить сеть? Пожалуйста, дополните. - person Julian Reschke; 11.02.2010
comment
Он поощряет шаблон, который будет применяться для решения проблем, вызванных плохим дизайном (например, плохой идентификацией ресурсов), а не просто использоваться там, где это действительно необходимо. Было достаточно проблем, удерживающих людей от POST, я ненавижу думать, куда мы идем с PATCH. - person Mike; 17.02.2010

Это нормально для POST и эмуляции PATCH, где это недоступно


Прежде чем объяснять это, вероятно, стоит упомянуть, что нет ничего плохого в использовании POST для выполнения общих обновлений (см. здесь) В частности:

POST становится проблемой только тогда, когда он используется в ситуации, для которой идеально подходит какой-либо другой метод: например, получение информации, которая должна быть представлением некоторого ресурса (GET), полная замена представления (PUT)< /эм>

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

Наш сервис должен быть открыт для сторонних продуктов, таких как SAP, Flex, Silverlight, Excel и т. д. Это означает, что мы должны использовать технологию с наименьшим общим знаменателем — какое-то время мы не могли использовать PUT, потому что только GET и POST поддерживались во всех клиентских технологиях.

Подход, который я использовал, состоит в том, чтобы иметь «_method=patch» как часть запроса POST. Преимущества:

(a) С этим легко справиться на стороне сервера — мы в основном делаем вид, что PATCH доступен

(b) Это указывает третьим сторонам, что мы не нарушаем REST, а работаем над ограничением браузера. Это также согласуется с тем, как PUT обрабатывалось сообществом Rails несколько лет назад, поэтому должно быть понятно многим.

(c) Его легко заменить, когда PATCH станет более доступным.

(d) Это прагматичный ответ на неловкую проблему.

person Chris McCauley    schedule 05.02.2010
comment
Наш сервис должен быть открыт для сторонних продуктов, таких как SAP, Flex, Silverlight, Excel и т. д. Это означает, что мы должны использовать технологию с наименьшим общим знаменателем — какое-то время мы не могли использовать PUT, потому что только GET и POST поддерживался во всех клиентских технологиях — прискорбно, но факт. Я думаю, что использование PUT с указанием того, что мы действительно хотим использовать PATCH, является разумным компромиссом. - person Chris McCauley; 06.02.2010
comment
PUT — плохой выбор, так как основное различие между небезопасными глаголами — идемпотентность. PUT является идемпотентным (как и DELETE), но PATCH не является идемпотентным (как POST), поэтому гораздо разумнее «перегружать» POST... конечно, идея «просто» использовать POST может быть идеей, поскольку это уже работает абсолютно нормально. , и фактическое «увеличение видимости», которое вы получите от введения такого метода, как PATCH, практически ничего не стоит и не стоит усилий или риска. - person Mike; 06.02.2010
comment
Различие, которое я считаю полезным, заключается в том, что для меня интуитивно понятно использовать PATCH непосредственно к URI ресурса. С помощью POST я бы предпочел размещать сообщения на соответствующем ресурсе обработки, который применяет обновления от моего имени. Я согласен, что это не большая разница, но я не вижу неправильного использования, которое вы делаете, так что я в порядке с этим :-) - person Darrel Miller; 06.02.2010

PATCH подходит для форматов patch или diff. А пока это совсем не полезно.

Что касается твоего решения 2 с кастомным методом, то ли в запросе, то ли в шапках, нет-нет-нет и нет, это ужасно :)

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

Все зависит от детализации ваших ресурсов и предполагаемых последствий кэширования.

person SerialSeb    schedule 08.02.2010
comment
вы можете использовать любой формат PATCH, который примет сервер. Я использую его, например, с x-www-form-urlencoded - person Nicholas Shanks; 08.11.2012

Немного поздно с ответом, но я бы подумал об использовании JSON Patch для таких сценариев.

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

Пример этого:

[
  { "op": "replace", "path": "/baz", "value": "boo" },
  { "op": "add", "path": "/hello", "value": ["world"] },
  { "op": "remove", "path": "/foo" }
]

Существует множество клиентских библиотек, которые могут

person Prabu    schedule 29.01.2019