Удалить несколько записей с помощью REST

100

Что такое REST-полный способ удаления нескольких элементов?

Мой вариант использования состоит в том, что у меня есть Backbone Collection, в которой мне нужно удалить несколько элементов одновременно. Варианты кажутся такими:

  1. Отправлять запрос DELETE для каждой отдельной записи (что кажется плохой идеей, если потенциально существует несколько десятков элементов);
  2. Отправьте DELETE, где идентификаторы для удаления объединены в URL (например, «/ records / 1; 2; 3»);
  3. Не в режиме REST отправьте пользовательский объект JSON, содержащий идентификатор, помеченный для удаления.

Все варианты далеко не идеальны.

Это похоже на серую зону соглашения REST.

Дональд Тейлор
источник
3
Возможный дубликат Restful способа удаления кучи элементов
Лука

Ответы:

93
  1. Это жизнеспособный вариант RESTful, но, очевидно, имеет описанные вами ограничения.
  2. Не делай этого. Посредники будут истолковывать это как означающее «УДАЛИТЬ (единственный) ресурс в /records/1;2;3». Таким образом, ответ 2xx на это может заставить их очистить свой кеш /records/1;2;3; не чистить /records/1, /records/2или /records/3; проксировать ответ 410 /records/1;2;3или другие вещи, которые не имеют смысла с вашей точки зрения.
  3. Это лучший выбор, и его можно сделать ОТДЫХАЮЩИЙ. Если вы создаете API и хотите разрешить массовые изменения ресурсов, вы можете использовать REST для этого, но для многих не сразу очевидно, как именно. Один из методов - создать ресурс «запрос на изменение» (например, путем POST-отправки тела, например, records=[1,2,3]to /delete-requests) и опросить созданный ресурс (указанный в Locationзаголовке ответа), чтобы узнать, был ли ваш запрос принят, отклонен, выполняется ли или завершил. Это полезно для длительных операций. Другой способ - отправить PATCHзапрос к ресурсу списка ,/records, тело которого содержит список ресурсов и действий, которые нужно выполнить с этими ресурсами (в любом формате, который вы хотите поддерживать). Это полезно для быстрых операций, когда код ответа на запрос может указывать на результат операции.

Все может быть достигнуто при сохранении ограничений REST, и обычно ответ состоит в том, чтобы превратить «проблему» в ресурс и дать ему URL.
Таким образом, пакетные операции, такие как удаление здесь, или отправка нескольких элементов в список, или внесение того же изменения в ряд ресурсов, могут выполняться путем создания списка «пакетных операций» и отправки в него новой операции.

Не забывайте, что REST - не единственный способ решить любую проблему. «ОТДЫХ» - это просто архитектурный стиль, и вам не обязательно его придерживаться (но вы теряете определенные преимущества Интернета, если вы этого не сделаете). Я предлагаю вам просмотреть этот список архитектур HTTP API и выбрать ту, которая вам подходит. Просто осознайте, что вы потеряете, если выберете другую архитектуру, и примите обоснованное решение на основе вашего варианта использования.

Есть несколько плохих ответов на этот вопрос о шаблонах для обработки пакетных операций в веб-службах REST? которые имеют слишком много голосов, но их тоже стоит прочитать.

Николас Шэнкс
источник
2
Вам нужно беспокоиться не о вашем сервере, а о посредниках, CDN, кеширующих прокси и т. Д. Интернет - это многоуровневая система. Вот почему он так хорошо работает. Рой определил, какие аспекты системы были необходимы для ее успеха, и назвал их REST. Если вы DELETEотправите запрос, все, что находится между запрашиваемым и сервером, будет думать, что один ресурс по указанному URL-адресу удаляется. Строки запроса являются непрозрачными частями URL-адреса для этих устройств, поэтому не имеет значения, как вы указываете свой API, они не посвящены этим знаниям, поэтому не могут вести себя иначе.
Николас Шанкс,
3
/ records / 1; 2; 3 не будет работать, если у вас есть много ресурсов для удаления из-за ограничений длины URI
dukethrash
3
Обратите внимание, что если рассматривать DELETE и тело, определяющее ресурсы для очистки, некоторые посредники могут не пересылать тело. Кроме того, некоторые HTTP-клиенты не могут добавить тело к DELETE. См stackoverflow.com/questions/299628/...
Люк Puplett
3
@LukePuplett Я бы просто сказал, что передача тела запроса с DELETEзапросом запрещена. Не делай этого. Если вы это сделаете, я съем ваших детей. Ням ням ням.
Николас Шанкс
3
Проблема с аргументом №3 заключается в том, что он несет такое же наказание, как и встречный аргумент против №2. Создание удаляемого ресурса - это не то, что прокси-серверы верхнего уровня будут знать, как обрабатывать - тот же аргумент счетчика, который выдвигается против подхода №2.
LB2 02
16

Если GET /records?filteringCriteriaвозвращает массив всех записей, соответствующих критериям, то DELETE /records?filteringCriteriaможет удалить все такие записи.

В этом случае ответ на ваш вопрос будет DELETE /records?id=1&id=2&id=3.

Мартин Ждила
источник
1
Я тоже пришел к такому выводу: просто переверните глагол на то, что вы хотите сделать. Я не понимаю, как то, что идет для GET, не подходит для DELETE.
Люк Пуплетт,
9
GET /records?id=1&id=2&id=3вовсе не означает , «получить три записи с идентификаторами 1, 2 и 3», это означает «получить один ресурс с URL путем / записями? ID = 1 & ID = 2 & ID = 3» , который может быть картиной репы, простой текст документ, содержащий число «42» на китайском языке, или может не существовать.
Николас Шанкс
Рассмотрим следующее: отправляются два последовательных запроса на /records?id=1и /records?id=2, а их ответы кэшируются некоторым посредником (например, вашим браузером или интернет-провайдером). Если бы Интернет знал, что ваше приложение имеет в виду под этим, то логично предположить, что запрос /records?id=1&id=2может быть возвращен кешем просто путем слияния (каким-то образом) двух результатов, которые у него уже есть, без необходимости запрашивать исходный сервер. Но это невозможно. /records?id=1&id=2может быть недействительным (разрешен только 1 идентификатор на запрос) или может возвращать что-то совершенно другое (репу).
Николас Шанкс
Это основная проблема кеширования ресурсов. Если мой администратор базы данных напрямую изменил состояние, кеши теперь не синхронизированы. Вы даете пример 410, возвращенный посредником, но 410 предназначен для постоянного удаления, после DELETE кеш может очистить свой слот для этого URL-адреса, но он не будет отправлять 410 или 404, поскольку он не знает, является ли администратор баз данных не просто немедленно вернул ресурс в исходное положение.
Люк Пуплетт 06
4
@NicholasShanks Я действительно не согласен. Если результаты кешируются, это вина сервера. И если вы говорите о дизайне API, надеюсь, вы пишете код для сервера. Независимо от того, используете ли вы строку запроса id[]=1&id[]=2или id=1&id=2в строке запроса для представления массива значений, эта строка запроса представляет именно это. И я думаю, что это очень распространенная и хорошая практика, когда строка запроса представляет собой фильтр. Кроме того, если вы разрешаете удаление и обновление, не кэшируйте GETзапросы. Если вы это сделаете, клиенты будут иметь устаревшее состояние.
Джозеф Нилдс,
10

Я думаю, что Mozilla Storage Service SyncStorage API v1.5 - хороший способ удалить несколько записей с помощью REST.

Удаляет всю коллекцию.

DELETE https://<endpoint-url>/storage/<collection>

Удаляет несколько BSO из коллекции одним запросом.

DELETE https://<endpoint-url>/storage/<collection>?ids=<ids>

ids : удаляет BSO из коллекции, чьи идентификаторы находятся в предоставленном списке, разделенном запятыми. Может быть предоставлено не более 100 идентификаторов.

Удаляет BSO в указанном месте.

DELETE https://<endpoint-url>/storage/<collection>/<id>

http://moz-services-docs.readthedocs.io/en/latest/storage/apis-1.5.html#api-instructions

вскоре
источник
Это кажется хорошим решением. Полагаю, если Mozilla считает, что это правильно, то так должно быть? Тогда единственный вопрос - это обработка ошибок. Предположим, они прошли? Ids = 1,2,3 и id 3 не существует. Удалите ли вы 1 и 2, а затем ответите 200, потому что запрашивающий хочет, чтобы 3 исчезло, а его нет, поэтому это не имеет значения? или что, если им разрешено удалять 1, но не 2 ... вы ничего не удаляете и отвечаете с ошибкой, или вы удаляете то, что можете, и оставляете остальные ...
tempcke
Обычно я возвращаю успешный ответ, потому что конечное состояние остается неизменным. Это также упрощает логику на клиенте, поскольку им больше не нужно обрабатывать это состояние ошибки. Что касается случая авторизации, я бы просто провалил весь запрос ... но на самом деле это зависит от вашего варианта использования.
Натан Петтплейс,
3

Это похоже на серую зону соглашения REST.

Да, до сих пор я встречал только одно руководство по проектированию REST API, в котором упоминаются пакетные операции (например, пакетное удаление): руководство по дизайну API Google .

В этом руководстве упоминается создание «пользовательских» методов, которые могут быть связаны через ресурс с помощью двоеточия, например https://service.name/v1/some/resource/name:customVerb, в нем также явно упоминаются пакетные операции как вариант использования:

Пользовательский метод может быть связан с ресурсом, коллекцией или службой. Он может принимать произвольный запрос и возвращать произвольный ответ, а также поддерживает потоковый запрос и ответ. [...] В пользовательских методах следует использовать команду HTTP POST, поскольку она имеет наиболее гибкую семантику [...] Для методов, критичных к производительности, может быть полезно предоставить пользовательские методы пакетной обработки, чтобы уменьшить накладные расходы на каждый запрос .

Итак, вы можете сделать следующее в соответствии с руководством Google по API:

POST /api/path/to/your/collection:batchDelete

... чтобы удалить кучу элементов из вашего коллекционного ресурса.

B12Toaster
источник
Является ли жизнеспособным решением, когда список элементов передается через массив в формате JSON?
Даниэле
Да, конечно. вы можете отправить полезную нагрузку POST, идентификаторы которой отправляются через массив json.
B12Toaster 05
Интересно, что руководство Google API сказано If the HTTP verb used for the custom method does not accept an HTTP request body (GET, DELETE), the HTTP configuration of such method must not use the body clause at all,в главе Custom Method. Но GET accounts.locations.batchGetapi - это метод GET с телом. Это странно. developers.google.com/my-business/reference/rest/v4/…
鄭元傑
@ 鄭元傑 согласен, на первый взгляд выглядит немного странно, но если вы присмотритесь, на самом деле используется POSThttp-метод, и назван только пользовательский метод batchGet. Я предполагаю, что Google делает это, чтобы (а) придерживаться своего правила, согласно которому все пользовательские методы должны быть POST(см. Мой ответ) и (б), чтобы людям было проще помещать «фильтр» в тело, чтобы вам не приходилось экранировать или закодировать фильтр, как со строками запроса. Обратной стороной, конечно же, является то, что это больше не кэшируется ...
B12Toaster
https://service.name/v1/some/resource/name:customVerbне является RESTful по определению.
deamon
2

Я допустил оптовую замену коллекции, например, PUT ~/people/123/shoesкогда тело является полным представлением коллекции.

Это работает для небольших дочерних коллекций элементов, где клиент хочет просмотреть элементы и удалить некоторые из них и добавить другие, а затем обновить сервер. Они могли ПОСТАВИТЬ пустую коллекцию, чтобы удалить все.

Это будет означать, GET ~/people/123/shoes/9что он все равно останется в кеше, даже если PUT удалил его, но это просто проблема кеширования, и это будет проблемой, если какой-то другой человек удалит обувь.

Мои API данных / систем всегда используют ETags, а не время истечения срока действия, поэтому сервер обрабатывается при каждом запросе, и мне требуются правильные заголовки версии / параллелизма для изменения данных. Для API, которые доступны только для чтения и выровнены по просмотру / отчету, я использую время истечения срока действия, чтобы уменьшить количество обращений к источнику, например, таблица лидеров может работать в течение 10 минут.

Для гораздо больших коллекций, например ~/people, мне не нужно многократное удаление, вариант использования обычно не возникает естественным образом, и поэтому одиночное DELETE работает нормально.

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

Я также позволил бы интерфейсным разработчикам создавать свои собственные API-интерфейсы, которые потребляют более строгие серверные API-интерфейсы, поскольку на стороне клиента часто есть практические и веские причины, по которым им не нравятся строгие конструкции REST API "Fielding zealot", а также для повышения производительности и Причины расслоения кеша.

Люк Пуплетт
источник
Мне понравился этот ответ до тех пор, пока я не прочитал последнее предложение :) Я никогда не видел случая, когда применение строгого REST имело чистый вредный эффект. Конечно, это может привести к написанию большего количества кода с обеих сторон, но в итоге вы получите более безопасную, чистую и менее связанную систему.
Николас Шэнкс
Ха-ха. Это действительно стало шаблоном! Бэкэнд для внешнего интерфейса называется технологическим радаром ThoughtWorks. Это также позволяет писать больше логики приложения, которая была бы громоздкой, скажем, в JavaScript, и, очевидно, могла бы быть обновлена ​​без клиента, например, для обновления приложения iOS.
Люк Пуплетт,
При беглом чтении первых четырех обращений от Google кажется, что этот метод BFF может работать только тогда, когда клиенты находятся под вашим контролем . Разработчики клиентов разрабатывают API, который им нужен, сопоставляя вызовы с API микросервисов, которые являются реальной серверной частью. На этой диаграмме: samnewman.io/patterns/architectural/bff/#bff Я бы поместил строку «Perimeter» под полями BFF - каждая ячейка является просто частью клиента. Он может даже находиться за пределами центра обработки данных, в котором размещены микросервисы. Я также не понимаю, как REST не применяется к обоим интерфейсам (клиент / BFF и BFF / микросервис).
Николас Шанкс
1
Да, это хороший момент. Обычно это происходит, когда у вас есть микросервисы для тимбилдинга и команда, создающая, например, приложение angular, и эта команда разработчиков представляет собой скорее интерфейсные типы, которым не нравится работать с кучей маленьких пуристических сервисов. Хотя я не вижу причин, по которым вы не можете использовать один и тот же шаблон для абстрагирования микросервисов и агрегации в более удобный фасад для ваших клиентов, чтобы микросервисы можно было изменять, не затрагивая фасад.
Люк Пуплетт
Конечная точка API должна моделировать потребности домена и бизнеса. Код для решения этих проблем и избежания чрезмерной инженерии, чтобы придерживаться строгих и часто негибких спецификаций. В любом случае REST - это не что иное, как рекомендации.
Викторио Берра