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


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

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


На самом деле в отношении 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, но правильно фильтруете удаление с помощью строки запроса.


-1

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

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

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


Это подход, который имеет для меня смысл: вы просто отправляете данные в одном запросе, но я всегда получаю ответ «но это не 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-заголовка.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.