Спокійний спосіб видалення купу предметів


98

У статті wiki для REST зазначено, що якщо ви використовуєте http://example.com/resources DELETE, це означає, що ви видаляєте всю колекцію.

Якщо ви використовуєте http://example.com/resources/7HOU57Y DELETE, це означає, що ви видаляєте цей елемент.

Я роблю ВЕБ-САЙТ, зверніть увагу НЕ ВЕБ-СЕРВІС.

У мене є список, який має 1 прапорець для кожного елемента у списку. Після того, як я вибрав кілька елементів для видалення, я дозволю користувачам натиснути кнопку, яка називається ВИДАЛИТИ ВИБОР. Якщо користувач натискає кнопку, відкриється діалогове вікно js з проханням підтвердити видалення. якщо користувач підтвердить, всі елементи видаляються.

Отже, як мені забезпечити видалення декількох елементів ВІДСТАВЛЕНО?

ПРИМІТКА. В даний час для ВИДАЛЕННЯ на веб-сторінці я використовую тег FORM з POST як дію, але включаю _метод зі значенням DELETE, оскільки саме це було зазначено іншими в SO про те, як зробити RESTful видалення для веб-сторінки .


1
Чи критично важливо, щоб ці видалення виконувались атомарно? Ви дійсно хочете скасувати видалення перших 30 елементів, якщо 31-й неможливо видалити?
Даррел Міллер,

@darrelmiller гарне запитання. Я думав, якщо видалення виконується атомарно, це буде менш ефективно. Отже, я схиляюся до ВИДАЛЕННЯ ІМЕНІ таблиці WHERE ID IN ({список ідентифікаторів)). Якщо хтось може вказати мені, чи це гарна ідея, або виправити мене. це було б дуже вдячно. Крім того, я не вимагаю зворотного видалення для перших 20 елементів, якщо 21-й буде видалений. Знову ж таки, я вдячний, якщо хтось може показати мені різницю в підході, де мені потрібно зробити реверс проти того, де мені НЕ потрібно робити реверс
Кім Стек

1
Примітка: для пункту "IN" можуть бути обмеження; наприклад, в Oracle ви можете ввести максимум 1000 ідентифікаторів.
грабувати

Дизайн API керівництво пропонує Google, рішення для створення призначених для користувача (партії) операцій в REST API, побачити моя відповідь тут: stackoverflow.com/a/53264372/2477619
B12Toaster

Відповіді:


54

Я думаю , що відповідь rojoca є найкращою на сьогодні. Можливі незначні варіації - скасувати підтвердження javascript на тій самій сторінці, а замість цього створити виділення та перенаправити на нього, показавши на цій сторінці повідомлення про підтвердження. Іншими словами:

З:
http://example.com/resources/

робити a

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?
Кім Стек

7
@fireeyeboy Цей двоступеневий підхід, здається, є таким загальноприйнятим способом виконання багатовидалення, але чому? Чому ви не просто надсилаєте запит на ВИДАЛЕННЯ на такий урі, http://example.com/resources/selections/а в корисному навантаженні (тілі) запиту ви надсилаєте дані, для яких елементів ви хочете видалити. Наскільки я можу зрозуміти, вам нічого не заважає це робити, але мене завжди зустрічають із "але це НЕ ВСІМ".
thecoshman

6
ВИДАЛИТИ потенційно може бути тіло ігнорується інфраструктури HTTP: stackoverflow.com/questions/299628 / ...
Люк Puplett

DELETE може мати тіло, але багато його реалізацій забороняли його тіло за замовчуванням
dmitryvim

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 пропонує створити екземпляр нового типу ресурсу, єдиним завданням якого є видалення списку ресурсів. Наприклад, у вас є колекція користувацьких ресурсів, і ви хочете видалити користувачів Боба, Дейва та Емі зі своєї колекції, тому ви створюєте новий ресурс Видалення, розмістивши Боб, Дейва та Емі як параметри створення. Створено ресурс "Видалення", який представляє асинхронний процес видалення Боба, Дейва та Емі з колекції "Користувачі".
Майк Туннікліфф,

1
Вибачте. Я все ще маю невеликі труднощі в розумінні кількох питань. DF4XY7. як, по суті, ти генеруєш цей рядок? Цей ресурс видалення. Чи потрібно вставляти будь-які дані в базу даних? Перепрошую, якщо повторю кілька запитань. Мені це просто трохи незнайомо.
Кім Стек

1
Я припускаю, що DF4XY7 - це сформований унікальний ідентифікатор, можливо, більш природно просто використовувати ідентифікатор, згенерований при збереженні в БД, наприклад example.com/resources/deletes/7. Моїм завданням було б створити модель видалення та зберегти її в базі даних; ви можете мати асинхронний процес видалення інших записів, щоб оновити модель видалення зі статусом завершення та будь-якими відповідними помилками.
Mike Tunnicliffe

2
@rojoca так, я думаю, проблема полягає в тому, що HTTP - це "ВИДАЛИТИ для видалення одного ресурсу". Що б ви не робили, отримання багаторазового видалення - це трохи хак. Ви все ще можете повернути клієнту `` роботу '', сказавши, що над цим завданням працює (і це може зайняти деякий час), але використовуйте цей URI для перевірки прогресу. Я прочитав специфікацію і зрозумів, що DELETE може мати тіло, як і інші запити.
thecoshman

33

Ось що Amazon зробила зі своїм API SEST REST.

Індивідуальний запит на видалення:

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 , Аналізувати сервер REST API і Google Drive API REST піти ще далі, дозволяючи «пакетні» окремі операції в одному запиті.

Ось приклад із сервера розбору.

Індивідуальний запит на видалення:

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

Я б сказав DELETE http://example.com/resources/id1,id2,id3,id4 або DELETE http://example.com/resources/id1+id2+id3+id4 . Оскільки "REST - це архітектурний (...) [не] протокол", щоб цитувати цю статтю Вікіпедії, я вважаю, що немає єдиного способу зробити це.

Я усвідомлюю, що вище неможливо без JS з HTML, але я відчуваю, що REST:

  • Створено, не замислюючись про незначні деталі, такі як транзакції. Кому потрібно буде оперувати більше, ніж один предмет? Це якось виправдано в протоколі HTTP, оскільки він не мав на меті подавати через нього будь-що інше, ніж статичні веб-сторінки.
  • Не потрібно добре пристосовуватися до сучасних моделей - навіть чистого HTML.

thx - що, якщо ви хочете видалити всю колекцію - чи слід тоді ідентифікатори пропустити?
BKSpurgeon

"У мене складається відчуття, що REST було ... створено, не замислюючись про такі дрібні деталі, як транзакції" - я не думаю, що це цілком вірно. Якщо я правильно розумію, у REST транзакції представлені ресурсами, а не методом. Існує хороша дискусія, яка завершується цим коментарем до цього повідомлення в блозі .
Пол Д. Уейт

10

Цікаво, що я думаю, що той самий метод застосовується як для PATCHing декількох об'єктів, і вимагає думати про те, що ми маємо на увазі під нашою 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 [читання] = 1

  1. Нарешті, щоб видалити кілька записів, ця кінцева точка є найбільш логічною:

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

Зрозумійте, я не вірю, що в цьому є якісь "правила" - для мене це просто "має сенс"


Власне щодо PATCH: оскільки це має означати часткове оновлення, якщо ви думаєте про список сутностей як про саму сутність (навіть якщо це масив типу), надсилаючи частковий масив (лише ідентифікатори, які ви хочете оновити) часткових сутностей, тоді ви може залишити рядок запиту, таким чином, не маючи URL-адреси, що представляє більше одного об’єкта.
Szabolcs Páll

2

Як відповідає відповідь Decent Dabbler та відповідь rojocas , найбільш канонічним є використання віртуальних ресурсів для видалення виділених ресурсів, але я вважаю, що це неправильно з точки зору REST, оскільки при виконанні a DELETE http://example.com/resources/selections/DF4XY7слід видалити сам ресурс виділення, а не вибрані ресурси.

Беручи відповідь Maciej Piechotka або відповідь fezfox , я маю лише заперечення: існує більш канонічний спосіб передачі масиву ідентифікаторів, і використовується оператор масиву:

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

Таким чином ви атакуєте кінцеву точку Видалити колекцію, але правильно фільтруєте видалення за допомогою рядка запитів.


-1

Оскільки немає "належного" способу зробити це, то, що я робив у минулому, є:

надішліть DELETE на http://example.com/something із кодованими даними xml або json у тілі.

одержавши запит, перевірте на ВИДАЛЕННЯ, якщо істинно, а потім прочитайте текст для тих, які потрібно видалити.


Це такий підхід, який для мене має сенс, ви просто надсилаєте дані в одному запиті, але я завжди отримую зустріч із "але це не RESTfull". Чи є у вас джерела, які припускають, що це життєздатний і “RESTfull” метод для цього?
thecoshman

10
Проблема цього підходу полягає в тому, що операції DELETE не передбачають отримання тіла, і тому деякі проміжні маршрутизатори в Інтернеті можуть видалити його для вас без вашого контролю та відома. Тож використовувати body для DELETE не безпечно!
Alex White

Посилання для Алекси коментар: stackoverflow.com/questions/299628 / ...
Люк Puplett

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
Коттон,

-1

У мене була однакова ситуація з видаленням кількох елементів. Це те, що я закінчив робити. Я використовував операцію DELETE, а ідентифікатори елементів, які потрібно було видалити, були частиною заголовка HTTP.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.