REST удалить несколько элементов в пакете

Мне нужно удалить несколько элементов по идентификатору в пакете, однако HTTP DELETE не поддерживает полезную нагрузку тела.

Варианты обхода:

1. @DELETE /path/abc?itemId=1&itemId=2&itemId=3 on the server side it will be parsed as List of ids and DELETE operation will be performed on each item.

2. @POST /path/abc including JSON payload containing all ids. { ids: [1, 2, 3] }

Насколько это плохо и какой вариант предпочтительнее? Любые альтернативы?

Обновление. Обратите внимание, что производительность является ключевым фактором, а не возможностью выполнения операции удаления для каждого отдельного идентификатора.


person Wild Goat    schedule 04.04.2019    source источник
comment
И то, и другое нецелесообразно, поскольку методы http в пакете, где часть пакета завершается сбоем, будут проблематичными при возврате результирующего кода состояния http. Альтернативой может быть возложение ответственности за пакетную операцию на клиента.   -  person Mr. Wrong    schedule 04.04.2019
comment
@Mr.Wrong, как клиент может нести ответственность за пакетную обработку? Весь смысл пакетной обработки заключается в том, чтобы оптимизировать процесс, а не выполнять запросы один за другим.   -  person Wild Goat    schedule 04.04.2019
comment
В дополнение к тому, что сказал г-н Неправильный, ваши два предложения предотвратят (промежуточный) кеш от аннулирования любого из сохраненных представлений ответа для вызванного URI, который в основном является ключом кеша, включая любой путь, матрицу или параметры запроса. Таким образом, запрос на GET /path/abc?itemId=1 может по-прежнему обслуживаться кешем, а не фактическим сервером, даже если фактический ресурс уже может быть удален в пакетном режиме.   -  person Roman Vottner    schedule 04.04.2019
comment
@RomanVottner Не уверен в этом, это то же самое, что и любая другая операция MUTATION. Если вы добавите несколько элементов в кеш категории A, для этой категории A необходимо обновить. То же самое с удалением. Если вы следуете своей логике, это означает, что вы вообще не можете использовать REST для массовых операций.   -  person Wild Goat    schedule 04.04.2019
comment
@WildGoat В то время как RFC 7234 говорит об аннулировании любой кэшированной информации, если операция изменения Кэш использует эффективный URI запроса для определения целевого ресурса. Обычно добавление новых элементов в коллекцию происходит через POST /path/to/collections, тогда как извлечение определенного элемента происходит через GET path/to/collections/item, который является ключом, отличным от того, который вы использовали для хранения новых элементов. Обновление или удаление этого конкретного элемента приведет к аннулированию кеша, однако OOTB.   -  person Roman Vottner    schedule 04.04.2019
comment
@WildGoat ... хотя ни одно из ваших предложений не нацелено на этот конкретный URI конкретного элемента, а только на тот, который находится в коллекции, управляющей элементом. Хотя кэширование является необязательным в отношении HTTP, Филдинг сделал кэширование фактически ограничение REST, которое поэтому должно поддерживаться вашими приложениями.   -  person Roman Vottner    schedule 04.04.2019


Ответы (2)


На протяжении многих лет многие люди сомневались в этом, как мы можем видеть в соответствующих вопросах здесь в стороне. Кажется, что принятые ответы варьируются от «обязательно сделайте это» до «это явно неправильное обращение с протоколом». Поскольку многие вопросы были отправлены много лет назад, давайте углубимся в спецификацию HTTP 1.1 от июня 2014 года (RFC 7231). ), чтобы лучше понять, что явно не рекомендуется, а что нет.

Первый предложенный обходной путь:

Во-первых, о ресурсах и самом URI в разделе 2:

Цель HTTP-запроса называется «ресурсом». HTTP не ограничивает характер ресурса; он просто определяет интерфейс, который может использоваться для взаимодействия с ресурсами. Каждый ресурс идентифицируется унифицированным идентификатором ресурса (URI).

Основываясь на этом, некоторые могут утверждать, что, поскольку HTTP не ограничивает характер ресурса, возможен URI, содержащий более одного id. Лично я считаю, что здесь дело в интерпретации.

Что касается вашего первого предложенного обходного пути (DELETE '/path/abc?itemId=1&itemId=2&itemId=3'), мы можем сделать вывод, что не рекомендуется думать о ресурсе как об отдельном документе в вашей коллекции сущностей, и в то же время хорошо, если вы думаете о ресурсе как о самой коллекции сущностей.

Второй предлагаемый обходной путь:

Что касается вашего второго предложенного обходного пути (POST '/path/abc' with body: { ids: [1, 2, 3] }), использование метода POST для удаления может ввести в заблуждение. В разделе Раздел 4.3.3 говорится о POST:

Метод POST запрашивает, чтобы целевой ресурс обрабатывал представление, включенное в запрос, в соответствии с собственной конкретной семантикой ресурса. Например, POST используется для следующих функций (среди прочего): Предоставление блока данных, таких как поля, введенные в HTML-форму, процессу обработки данных; Публикация сообщения на доске объявлений, в группе новостей, в списке рассылки, в блоге или в аналогичной группе статей; Создание нового ресурса, который еще не идентифицирован исходным сервером; и добавление данных к существующему представлению (представлениям) ресурса.

Хотя есть некоторое пространство для интерпретации «среди прочих» функций для POST, это явно противоречит тому факту, что у нас есть метод DELETE для удаления ресурсов, как мы можем видеть в Раздел 4.1:

Метод DELETE удаляет все текущие представления целевого ресурса.

Поэтому лично я настоятельно не рекомендую использовать POST для удаления ресурсов.

Альтернативный обходной путь:

Вдохновленный вашим вторым обходным путем, мы предлагаем еще один:

DELETE '/path/abc' with body: { ids: [1, 2, 3] }

Это почти то же самое, что предложено во втором обходном пути, но вместо этого используется правильный метод HTTP для удаления. Здесь мы приходим к путанице с использованием объекта body в запросе DELETE. Есть много людей, заявляющих, что это недействительно, но давайте придерживаться Раздел 4.3.5 спецификации:

Полезная нагрузка в сообщении запроса DELETE не имеет определенной семантики; отправка тела полезной нагрузки по запросу DELETE может привести к тому, что некоторые существующие реализации отклонят запрос.

Итак, мы можем сделать вывод, что спецификация не запрещает DELETE иметь полезную нагрузку body. К сожалению, некоторые существующие реализации могут отклонить запрос... Но как это влияет на нас сегодня?

Трудно быть уверенным на 100%, но современный запрос, сделанный с помощью fetch, просто не позволяет использовать body для GET и HEAD. Это то, что указано в стандарте Fetch по адресу Раздел 5.3 по пункту 34:

Если какое-либо тело существует и не равно нулю, или inputBody не равно нулю, а метод запроса — GET или HEAD, то генерируется TypeError.

И мы можем подтвердить, что он реализован таким же образом для fetch pollyfill на строка 342.

Заключительные советы:

Поскольку альтернативный обходной путь с DELETE и полезной нагрузкой body допускается спецификацией HTTP и поддерживается всеми современными браузерами с fetch и начиная с IE10 с полифиллом, я рекомендую этот способ для пакетного удаления допустимым и полностью рабочим способом.

person Erick Petrucelli    schedule 04.04.2019
comment
Присяжные не совсем согласны с тем, что делать с DELETE и телом запроса, но rfc определенно в настоящее время не означает, что тело запроса на DELETE вообще имеет семантическое значение, хотя это распространенная путаница. Это может измениться, но на данный момент цель состоит в том, чтобы тело запроса на DELETE могло появиться, но это не имеет смысла. - person Evert; 04.04.2019
comment
Недавно я рассказал об этом группе, которая разрабатывает следующую версию стандарта HTTP. Я бы определенно посоветовал воздержаться от использования DELETE до тех пор, пока он не будет действительно санкционирован рабочей группой http: https://github.com/httpwg/http-core/issues/202 - person Evert; 04.04.2019
comment
@Evert Однако это будет работать только на HTTP-клиентах/серверах, которые также используют эту новую версию протокола. Многие серверы/клиенты не будут поддерживать такую ​​функцию в ближайшие годы. В дополнение к этому неясно, как пакетное удаление должно информировать промежуточные кэши о аннулировании сохраненных ответов для тех элементов, которые были удалены в пакетном запросе. В его текущей форме некоторые серверы, которые игнорируют полезную нагрузку, могут удалить всю коллекцию, а не только те несколько упомянутых элементов. В конце концов может понадобиться четко определенная новая операция (BATCH-DELETE)?! - person Roman Vottner; 04.04.2019
comment
Я думаю, что этот ответ заслуживает одобрения, потому что все критикуют массовый подход, но никто не предлагает решения. Очевидно, что в этом есть необходимость! - person Wild Goat; 04.04.2019
comment
@WildGoat Самое главное, чего не хватает в этом вопросе, - это объяснение того, почему в первую очередь необходимо пакетное удаление. Если вам нужно удалить список ресурсов, канонический способ сделать это — использовать несколько HTTP-запросов. Требуя тело, вы просто передаете семантику HTTP в тело. В чем основная проблема? - person Evert; 04.04.2019
comment
@Evert Основная проблема - это производительность. Очевидно, я могу сделать это один за другим в цикле for, но удаление 1000 элементов и открытие/закрытие соединений с БД 1000 раз не вариант. Надеюсь, это имеет смысл? - person Wild Goat; 04.04.2019
comment
@WildGoat Я обычно держу соединения с базой данных объединенными и открытыми. Я предполагаю, что вы работаете с языком/фреймворком, где это невозможно? Одной из идей может быть отправка всего в очередь и выполнение пакетной обработки вне основного потока HTTP. - person Evert; 04.04.2019
comment
В любом случае, если вы совершенно не заинтересованы в творческом подходе, сохраняющем правильную семантику HTTP, правильное решение — использовать POST с настраиваемым телом запроса, указанным здесь как «Второе предложение». - person Evert; 04.04.2019
comment
@WildGoat В дополнение к тому, что сказал Эверт, HTTP-соединения могут быть конвейерными а также позволяет вам отправлять несколько запросов в пакетном режиме по одному и тому же соединению, т. е. каждый нацелен на один конкретный элемент для удаления. Здесь вы можете легко использовать DELETE в URI, который нацелен на конкретный элемент, вместо управления ресурсом коллекции. - person Roman Vottner; 04.04.2019
comment
Ребята, я более чем готов следовать правильной сематинтике HTTP, однако ни один из этих подходов не является достаточно осуществимым с точки зрения производительности или усилий по разработке. Этот модуль является подключаемым модулем к существующей архитектуре, реально мы не можем вызывать API 1000 раз один за другим, и, очевидно, у нас нет человеческих ресурсов для реализации творческого решения, нам просто нужно выполнить работу, выполняя это массово. - person Wild Goat; 04.04.2019

Важно понимать, что методы HTTP работают в области «передачи документов по сети», а не в вашей собственной области.

Ваша модель ресурсов — это не ваша модель предметной области, а не ваша модель данных.

Альтернативное написание: REST API — это прикрытие, позволяющее сделать ваш домен похожим на веб-сайт.

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

DELETE /path/abc?itemId=1&itemId=2&itemId=3

Таким образом, в этом HTTP-запросе конкретно говорится: «Применить семантику удаления к документу, описанному /path/abc?itemId=1&itemId=2&itemId=3». Тот факт, что этот документ состоит из трех различных элементов в вашем хранилище длительного пользования, каждый из которых необходимо удалять независимо, является деталями реализации. Частью смысла REST является то, что клиенты изолированы от именно такого рода знаний.

Однако, и я чувствую, что многие люди теряются, метаданные, возвращаемые ответом на этот запрос на удаление, не сообщают клиенту ничего о ресурсах с разными идентификаторами.

Что касается клиента, /path/abc является идентификатором, отличным от /path/abc?itemId=1&itemId=2&itemId=3. Итак, если клиент выполнил GET для /path/abc и получил представление, включающее itemId 1, 2, 3; а затем отправляет описанное вами удаление, он все равно будет иметь в своем собственном кеше представление, которое включает /path/abc после успешного удаления.

Это может быть, а может и не быть тем, чего вы хотите. Если вы используете REST (через HTTP), это то, о чем вы должны думать в своем дизайне.

POST /path/abc

some-useful-payload

Этот метод сообщает клиенту, что мы вносим некоторые (возможно, небезопасные) изменения в /path/abc, и если это удастся, предыдущее представление должно быть аннулировано. Клиент должен повторить свой предыдущий запрос GET /path/abc, чтобы обновить свое предыдущее представление, а не использовать любую более раннюю недействительную копию.

Но по-прежнему не влияет на кешированные копии других ресурсов.

/path/abc/1
/path/abc/2
/path/abc/3

Все они все еще будут находиться в кеше, даже если они были «удалены».

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

Еще раз: ваша модель ресурсов — это не ваша модель предметной области, а не ваша модель данных. REST API — это другой способ осмысления того, что происходит, а архитектурный стиль REST настроен для решения конкретной проблемы и, следовательно, может не подходить для более простой задачи, которую вы пытаетесь решить.

Это не означает, что я считаю, что каждый должен проектировать свои собственные системы в соответствии с архитектурным стилем REST. REST предназначен для долгоживущих сетевых приложений, охватывающих несколько организаций. Если вы не видите необходимости в ограничениях, не используйте их. Меня это устраивает, если вы не называете результат REST API. У меня нет проблем с системами, которые соответствуют их собственному архитектурному стилю. -- Филдинг, 2008 г.

person VoiceOfUnreason    schedule 04.04.2019