Видаліть кілька записів за допомогою REST


97

Який НАЙБІЛЬШИЙ спосіб видалення декількох елементів?

Моїм випадком використання є те, що у мене є Backbone Collection, де мені потрібно мати можливість видаляти кілька елементів одночасно. Варіанти, здається, такі:

  1. Надіслати запит на ВИДАЛЕННЯ для кожного окремого запису (що здається поганою ідеєю, якщо потенційно є десятки елементів);
  2. Надіслати ВИДАЛИТИ, де ідентифікатори, які потрібно видалити, нанесені в URL-адресу (тобто, "/ records / 1; 2; 3");
  3. Відправте нестандартний об'єкт JSON, що містить ідентифікатори, позначені для видалення.

Всі варіанти менш ніж ідеальні.

Це здається сірою зоною Конвенції REST.


Відповіді:


92
  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 для цього, але саме те, що багатьом не відразу очевидно. Одним із методів є створення ресурсу "запиту на зміну" (наприклад, шляхом розміщення такого тіла, якrecords=[1,2,3] to /delete-requests) та опитування створеного ресурсу (зазначеного в Locationзаголовку відповіді), щоб з'ясувати, чи ваш запит прийнято, відхилено чи триває або завершив. Це корисно для тривалих операцій. Інший спосіб - надіслати PATCHзапит до ресурсу списку ,/records, основна частина якої містить перелік ресурсів та дій, які потрібно виконати з цими ресурсами (в будь-якому форматі, який ви хочете підтримати). Це корисно для швидких операцій, де код відповіді на запит може вказувати на результат операції.

Все можна досягти, дотримуючись обмежень REST, і зазвичай відповідь полягає в тому, щоб перетворити "проблему" на ресурс і вказати йому URL-адресу.
Отже, пакетні операції, такі як видалення тут, розміщення декількох елементів у списку або внесення однакових змін у сукупність ресурсів, можна виконати, створивши список "пакетних операцій" та розмістивши в ньому нову операцію.

Не забувайте, REST - це не єдиний спосіб вирішити будь-яку проблему. “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,

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 посвідчення особи) або може повернутися щось зовсім інше (ріпка).
Ніколас Шенкс,

Це основна проблема кешування ресурсів. Якщо мій DBA мутував стан безпосередньо, то кеші зараз не синхронізовані. Ви даєте приклад 410, який повертає посередник, але 410 призначений для постійного видалення, після ВИДАЛЕННЯ кеш може очистити своє місце для цієї URL-адреси, але він не надішле 410 або 404, оскільки не знає, чи є DBA не просто повернув ресурс відразу ж назад, у джерелі.
Люк Пуплетт

4
@NicholasShanks Я дійсно не згоден. Якщо результати кешовані, це винна серверу. І якщо ви говорите про дизайн API, сподіваємось, ви пишете код для сервера. Незалежно від того, використовуєте ви id[]=1&id[]=2або id=1&id=2в рядку запиту для представлення масиву значень, цей рядок запиту представляє саме це. І я думаю, що це надзвичайно поширена та хороша практика, коли рядок запиту представляє фільтр. Крім того, якщо ви дозволяєте видаляти та оновлювати, не кешуйте GETзапити. Якщо ви це зробите, клієнти матимуть застарілий стан.
Джозеф Нілдс

8

Я думаю, що 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 ... ти нічого не
видаляєш

Зазвичай я повертаю успішну відповідь, оскільки кінцевий стан однаковий незалежно. Це також спрощує логіку на клієнті, оскільки їм більше не потрібно обробляти такий стан помилок. Що стосується випадку авторизації, я б просто провалив весь запит ... але насправді це залежить від вашого випадку використання.
Натан Феттеплас

3

Це здається сірою зоною Конвенції REST.

Так, поки що я зіткнувся лише з одним керівництвом з дизайну REST API, в якому згадуються пакетні операції (наприклад, пакетне видалення): керівництво по розробці API API Google .

У цьому посібнику згадується про створення "користувацьких" методів, які можуть бути пов'язані через ресурс за допомогою двокрапки, наприклад https://service.name/v1/some/resource/name:customVerb, у ньому також прямо згадуються пакетні операції як варіант використання:

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

Отже, ви можете зробити наступне згідно з посібником API Google:

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

... щоб видалити купу елементів вашого ресурсу колекції.


Чи є життєздатним рішенням, що перелік елементів передається за допомогою відформатованого масиву JSON?
Даніеле

так, авжеж. Ви можете розмістити корисне навантаження, в якому ідентифікатори надсилаються через масив json.
B12Тостер

Цікаво, що посібник 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,у розділі Спеціальний метод. Але GET accounts.locations.batchGetapi - це метод GET з тілом. Це дивно. developers.google.com/my-business/reference/rest/v4/…
鄭元傑

@ 鄭元傑 погодьтеся, з першого погляду це виглядає трохи дивно, але якщо ви уважно подивитесь, то насправді POSTвикористовується метод http і названий лише спеціальний метод batchGet. Я думаю, Google робить це, щоб (а) дотримуватися їх правила, згідно з яким мають бути всі власні методи POST(див. Мою відповідь) та (б), щоб полегшити людям введення "фільтра" в тіло, щоб вам не потрібно було екранувати або кодувати фільтр, як із рядками запитів. недоліком, звичайно, є те, що це насправді більше не можна кешувати ...
B12Toaster

https://service.name/v1/some/resource/name:customVerbне є RESTful за визначенням.
Дімон

2

Я дозволив здійснити оптову заміну колекції, наприклад PUT ~/people/123/shoes де основним є колекція.

Це працює для невеликих дочірніх колекцій елементів, де клієнт хоче переглянути елементи та обрізати деякі, а також додати інші, а потім оновити сервер. Вони можуть ПОСТАВИТИ порожню колекцію, щоб видалити всі.

Це означало GET ~/people/123/shoes/9б, що він все ще залишатиметься в кеш-пам'яті, навіть незважаючи на те, що PUT видалив його, але це лише проблема кешування і буде проблемою, якщо хтось інший видалить взуття.

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

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

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

Я б також дозволив розробникам інтерфейсних систем створювати власні API, які споживають більш суворі інтерфейси API, оскільки часто бувають практичні, обгрунтовані причини на стороні клієнта, чому їм не подобається строгий дизайн API REST API "Fielding zealot", а також для підвищення продуктивності та причини шарування кешу.


Мені сподобалась ця відповідь аж до прочитання останнього речення :) Я ніколи не бачив випадків використання, коли застосування суворого REST мало чистий згубний ефект. Звичайно, це може призвести до написання більше коду з обох кінців, але в підсумку ви отримаєте більш безпечну, чисту та менш пов’язану систему.
Ніколас Шенкс,

Ха-ха. Це насправді стало зразком! Бекенд для інтерфейсу він називається на технологічному радарі ThoughtWorks. Це також дозволяє писати більше логіки додатків, яка була б громіздкою, скажімо, в JavaScript, і, очевидно, може бути оновлена ​​без клієнта, так, скажімо, оновлення для програми iOS.
Люк Пуплетт

Читаючи перші чотири звернення від Google, здається, що ця методика BFF може працювати лише тоді, коли клієнти перебувають під вашим контролем . Клієнтські розробники розробляють API, який вони хочуть, зіставляючи виклики до мікросервісних API, які є справжнім зворотним кінцем. На цій схемі: samnewman.io/patterns/architectural/bff/#bff я розмістив би рядок "Периметр" під полями BFF - кожен ящик просто є частиною клієнта. Він міг жити навіть за межами центру обробки даних, в якому розміщені мікросервіси. Я також не бачу, як REST не застосовується до обох інтерфейсів (client / BFF та BFF / microservice).
Ніколас Шенкс,

1
Так, це хороший момент. Зазвичай це, коли у вас є мікрослужби для створення команд і команда, що робить кутовий додаток, наприклад, і ця команда розробників - це більше типів інтерфейсів, яким не подобається працювати проти кучки маленьких пуристичних служб. Хоча я не бачу жодної причини, по якій ви не можете використовувати той самий шаблон для абстрагування мікропослуг та агрегації у більш зручний фасад для ваших клієнтів, так що мікропослуги можуть бути змінені, не впливаючи на фасад.
Luke Puplett

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