Удобный способ удаления кучи элементов

98

В вики-статье для REST указано, что если вы используете http://example.com/resources DELETE, это означает, что вы удаляете всю коллекцию.

Если вы используете http://example.com/resources/7HOU57Y DELETE, это означает, что вы удаляете этот элемент.

Я делаю ВЕБ-САЙТ, обратите внимание, НЕ ВЕБ-СЕРВИС.

У меня есть список, в котором есть 1 флажок для каждого элемента в списке. Как только я выберу несколько элементов для удаления, я разрешаю пользователям нажимать кнопку УДАЛИТЬ ВЫБОР. Если пользователь нажмет кнопку, появится диалоговое окно js с просьбой подтвердить удаление. если пользователь подтверждает, все элементы удаляются.

Итак, как мне обеспечить УДОБНОЕ удаление нескольких элементов?

ПРИМЕЧАНИЕ, в настоящее время для DELETE на веб-странице я использую тег FORM с POST в качестве действия, но включаю _method со значением DELETE, поскольку это то, что было указано другими в SO о том, как выполнить удаление RESTful для веб-страницы .

Ким Стэкс
источник
1
Важно ли, чтобы эти удаления выполнялись атомарно? Вы действительно хотите отменить удаление первых 30 элементов, если 31-й не может быть удален?
Даррел Миллер,
@darrelmiller хороший вопрос. Я подумал, что если удаление будет выполняться атомарно, это будет менее эффективно. Следовательно, я склоняюсь к УДАЛЕНИЮ ИЗ tablename WHERE ID IN ({список идентификаторов}). Если кто-то может указать мне, хорошая ли это идея, или поправьте меня. это было бы хорошо оценено. Также я не требую обратного удаления для первых 20 элементов, если удаляется 21-й. Опять же, я ценю, если кто-то может показать мне разницу в подходе, где мне нужно повернуть вспять, и где мне НЕ нужно обратное,
Ким Стэкс
1
Примечание: могут быть ограничения для предложения «IN»; например, в Oracle вы можете поместить максимум 1000 идентификаторов.
ограбить
Руководство по проектированию API Google предлагает решение для создания настраиваемых (пакетных) операций в REST API, см. Мой ответ здесь: stackoverflow.com/a/53264372/2477619
B12Toaster

Ответы:

54

Я думаю ответ Рохоки пока самый лучший. Небольшое изменение может заключаться в том, чтобы отказаться от подтверждения javascript на той же странице и вместо этого создать выделение и перенаправить на него, показывая сообщение подтверждения на этой странице. Другими словами:

Из:
http://example.com/resources/

сделать

POST с выбором идентификаторов для:
http://example.com/resources/selections

который в случае успеха должен ответить:

HTTP / 1.1 201 создан и заголовок Location для:
http://example.com/resources/selections/DF4XY7

На этой странице вы увидите окно подтверждения (javascript), которое, если вы подтвердите, выполнит запрос:

УДАЛИТЬ http://example.com/resources/selections/DF4XY7

который в случае успеха должен ответить: HTTP / 1.1 200 Ok (или что-то еще, подходящее для успешного удаления)

Достойный дилетант
источник
Мне нравится эта идея, потому что вам не нужны перенаправления. Включив AJAX, вы можете делать все это, не покидая страницы.
Rojoca
После этого УДАЛИТЬ example.com/resources/selections/DF4XY7 , буду ли я перенаправлен обратно на example.com/resources?
Kim Stacks
7
@fireeyeboy Этот двухэтапный подход кажется очень часто предлагаемым способом выполнения множественного удаления, но почему? Почему вы просто не отправляете запрос DELETE в uri, как http://example.com/resources/selections/и в полезной нагрузке (теле) запроса, вы отправляете данные, для которых вы хотите удалить элементы. Насколько я могу судить, ничто не мешает вам это делать, но меня всегда встречают «но это не RESTfull».
thecoshman
6
DELETE потенциально может игнорировать тело инфраструктуры HTTP: stackoverflow.com/questions/299628/…
Люк Пуплет,
DELETE может иметь тело, но многие его реализации по умолчанию запрещают его тело
дмитривим
54

Один из вариантов - создать «транзакцию» удаления. Итак, вы POSTпопали в нечто вроде http://example.com/resources/deletesнового ресурса, состоящего из списка ресурсов, которые нужно удалить. Затем в своем приложении вы просто выполняете удаление. Когда вы делаете пост , вы должны вернуть расположение вашей созданной транзакции , например, http://example.com/resources/deletes/DF4XY7. A GETпри этом может вернуть статус транзакции (завершена или выполняется) и / или список ресурсов, которые необходимо удалить.

Рохока
источник
2
Ничего общего с вашей базой данных. Под транзакцией я подразумеваю просто список операций, которые нужно выполнить. В данном случае это список удалений. Что вы делаете, так это создаете новый список (удалений) в качестве ресурса в вашем приложении. Ваше веб-приложение может обрабатывать этот список как угодно. У этого ресурса есть URI, например example.com/resources/deletes/DF4XY7 . Это означает, что вы можете проверить статус удаления с помощью GET для этого URI. Это было бы удобно, если при удалении вам нужно было удалить изображения из Amazon S3 или другого CDN, и эта операция может занять много времени.
Rojoca
2
+1 это хорошее решение. Вместо отправки DELETE каждому ресурсу @rojoca предлагает создать экземпляр нового типа ресурса, единственной задачей которого является удаление списка ресурсов. Например, у вас есть коллекция пользовательских ресурсов, и вы хотите удалить пользователей Боба, Дэйва и Эми из своей коллекции, поэтому вы создаете новый ресурс для удаления, отправляя Боба, Дэйва и Эми в качестве параметров создания. Ресурс Deletion создан и представляет собой асинхронный процесс удаления Боба, Дэйва и Эми из коллекции Users.
Майк Танниклифф,
1
Я прошу прощения. У меня все еще есть небольшие трудности с пониманием некоторых вопросов. DF4XY7. как, черт возьми, вы генерируете эту строку? Это удаление ресурса. Нужно ли мне вставлять какие-либо данные в базу данных? Прошу прощения, если повторю несколько вопросов. Просто мне это немного незнакомо.
Ким Стэкс
1
Я предполагаю, что DF4XY7 - это сгенерированный уникальный идентификатор, возможно, более естественным будет просто использовать идентификатор, сгенерированный при сохранении в БД, например example.com/resources/deletes/7. Я хотел бы создать модель удаления и сохранить ее в базе данных, вы можете использовать асинхронный процесс, удаляющий другие записи, обновляя модель удаления со статусом завершения и любыми соответствующими ошибками.
Майк Танниклифф,
2
@rojoca да, я думаю, проблема в том, что HTTP очень похож на «DELETE для удаления одного ресурса». Что бы вы ни делали, получение множественного удаления - это своего рода взлом. Вы по-прежнему можете вернуть «задание» клиенту, заявив, что эта задача выполняется (и это может занять некоторое время), но использовать этот URI для проверки хода выполнения. Я прочитал спецификацию и понял, что DELETE может иметь тело, как и другие запросы.
thecoshman
33

Вот что Amazon сделала со своим S3 REST API.

Индивидуальный запрос на удаление:

DELETE /ObjectName HTTP/1.1
Host: BucketName.s3.amazonaws.com
Date: date
Content-Length: length
Authorization: authorization string (see Authenticating Requests (AWS Signature Version 4))

Удаление нескольких объектовЗапрос на :

POST /?delete HTTP/1.1
Host: bucketname.s3.amazonaws.com
Authorization: authorization string
Content-Length: Size
Content-MD5: MD5

<?xml version="1.0" encoding="UTF-8"?>
<Delete>
    <Quiet>true</Quiet>
    <Object>
         <Key>Key</Key>
         <VersionId>VersionId</VersionId>
    </Object>
    <Object>
         <Key>Key</Key>
    </Object>
    ...
</Delete>           

Но Facebook Graph API , Parse Server REST API и Google Drive REST API идут еще дальше, позволяя «группировать» отдельные операции в одном запросе.

Вот пример из Parse Server.

Индивидуальный запрос на удаление:

curl -X DELETE \
  -H "X-Parse-Application-Id: ${APPLICATION_ID}" \
  -H "X-Parse-REST-API-Key: ${REST_API_KEY}" \
  https://api.parse.com/1/classes/GameScore/Ed1nuqPvcm

Пакетный запрос:

curl -X POST \
  -H "X-Parse-Application-Id: ${APPLICATION_ID}" \
  -H "X-Parse-REST-API-Key: ${REST_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{
        "requests": [
          {
            "method": "POST",
            "path": "/1/classes/GameScore",
            "body": {
              "score": 1337,
              "playerName": "Sean Plott"
            }
          },
          {
            "method": "POST",
            "path": "/1/classes/GameScore",
            "body": {
              "score": 1338,
              "playerName": "ZeroCool"
            }
          }
        ]
      }' \
  https://api.parse.com/1/batch
Лука Житник
источник
13

Я бы сказал УДАЛИТЬ http://example.com/resources/id1,id2,id3,id4 или УДАЛИТЬ http://example.com/resources/id1+id2+id3+id4 . Поскольку «REST - это архитектура (...) [не] протокол», цитируя эту статью в Википедии, я считаю, что нет единого способа сделать это.

Я знаю, что это невозможно без JS с HTML, но я чувствую, что REST был:

  • Создан без учета мелких деталей, таких как транзакции. Кому потребуется оперировать более чем одним предметом? Это как-то оправдано в протоколе HTTP, поскольку он не предназначался для обслуживания чего-либо еще, кроме статических веб-страниц.
  • Нет необходимости хорошо подстраиваться под текущие модели - даже на чистый HTML.
Мацей Пехотка
источник
спасибо - что, если вы хотите удалить всю коллекцию - следует ли тогда опускать идентификаторы?
BKSpurgeon
«У меня такое ощущение, что REST ... был создан без учета мелких деталей, таких как транзакции» - я не думаю, что это совсем так. Если я правильно понимаю, в REST транзакции представлены ресурсами, а не методом. Этот комментарий к этому сообщению в блоге завершается хорошей дискуссией .
Пол Д. Уэйт
10

Интересно, что я думаю, что тот же метод применяется к ПАТЧУ нескольких объектов и требует размышлений о том, что мы подразумеваем под нашим URL, параметрами и методом REST.

  1. вернуть все элементы 'foo':

    [GET] api/foo

  2. вернуть элементы 'foo' с фильтрацией по определенным идентификаторам:

    [GET] api/foo?ids=3,5,9

В этом смысле URL и фильтр определяют «с какими элементами мы имеем дело?», А метод REST (в данном случае «GET») говорит «что делать с этими элементами?»

  1. Следовательно, PATCH несколько записей, чтобы пометить их как прочитанные

    [PATCH] api/foo?ids=3,5,9

..с данными foo [read] = 1

  1. Наконец, для удаления нескольких записей наиболее логична эта конечная точка:

    [DELETE] api/foo?ids=3,5,9

Пожалуйста, поймите, я не верю, что в этом есть какие-то «правила» - для меня это просто «имеет смысл»

Fezfox
источник
На самом деле в отношении PATCH: поскольку это означает частичное обновление, если вы думаете о списке сущностей как о самой сущности (даже если это массив типов), отправляя частичный массив (только идентификаторы, которые вы хотите обновить) частичных сущностей, тогда вы может не включать строку запроса, таким образом не имея URL-адреса, представляющего более одного объекта.
Сабольч Палл
2

Как Достойный ответ Dabbler и rojocas ответ говорят, наиболее канонический используют виртуальные ресурсы , чтобы удалить выделение ресурсов, но я считаю , что это неправильно с точки зрения REST, потому исполняющимDELETE http://example.com/resources/selections/DF4XY7 должно удалить сам ресурс выбора, а не выбранные ресурсов.

Принимая ответ Maciej Piechotka anwser или fezfox , у меня есть только возражение: существует более канонический способ передачи массива идентификаторов с использованием оператора массива:

DELETE /api/resources?ids[]=1a2b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d&ids[]=7e8f9a0b-1c2d-3e4f-5a6b-7c8d9e0f1a2b

Таким образом, вы атакуете конечную точку Delete Collection, но правильно фильтруете удаление с помощью строки запроса.

Mangelsnc
источник
-1

Поскольку нет «правильного» способа сделать это, я делал в прошлом следующее:

отправить DELETE на http://example.com/something с данными в кодировке xml или json в теле.

когда вы получите запрос, проверьте DELETE, если true, затем прочитайте тело тех, которые нужно удалить.

user103219
источник
Это подход, который имеет для меня смысл: вы просто отправляете данные в одном запросе, но я всегда получаю ответ «но это не RESTfull». Есть ли у вас источники, предполагающие, что это жизнеспособный и RESTfull-метод для этого?
thecoshman
10
Проблема с этим подходом заключается в том, что операции DELETE не ожидают тела, поэтому некоторые из промежуточных маршрутизаторов в Интернете могут удалить его для вас без вашего контроля или ведома. Поэтому использование body для DELETE небезопасно!
Alex White
Ссылка на комментарий Алекса: stackoverflow.com/questions/299628/…
Люк Пуплет
1
A payload within a DELETE request message has no defined semantics; sending a payload body on a DELETE request might cause some existing implementations to reject the request.из tools.ietf.org/html/rfc7231#section-4.3.5
cottton
-1

У меня была такая же ситуация с удалением нескольких элементов. Вот что я в итоге сделал. Я использовал операцию DELETE, и идентификаторы элементов, которые должны были быть удалены, были частью HTTP-заголовка.

Шерин Сирийский
источник