Usuń wiele rekordów za pomocą REST

100

Jaki jest sposób usuwania wielu elementów w trybie REST?

Moim przypadkiem jest to, że mam kolekcję Backbone, w której muszę mieć możliwość jednoczesnego usuwania wielu elementów. Wydaje się, że opcje są następujące:

  1. Wyślij żądanie DELETE dla każdego rekordu (co wydaje się złym pomysłem, jeśli potencjalnie są dziesiątki elementów);
  2. Wyślij DELETE, gdzie identyfikatory do usunięcia są połączone razem w adresie URL (tj. „/ Records / 1; 2; 3”);
  3. W sposób inny niż REST wyślij niestandardowy obiekt JSON zawierający identyfikatory oznaczone do usunięcia.

Wszystkie opcje są mniej niż idealne.

Wydaje się, że jest to szara strefa konwencji REST.

Donald Taylor
źródło
3
Możliwy duplikat Restful way do usuwania wielu elementów
Luka Žitnik

Odpowiedzi:

92
  1. To realny wybór RESTful, ale oczywiście ma ograniczenia, które opisałeś.
  2. Nie rób tego. Zostałoby to zinterpretowane przez pośredników jako oznaczające „USUŃ (pojedynczy) zasób w /records/1;2;3” - więc odpowiedź 2xx na to może spowodować wyczyszczenie pamięci podręcznej /records/1;2;3; nie czyścić /records/1, /records/2lub /records/3; proxy odpowiedzi 410 dla /records/1;2;3lub innych rzeczy, które nie mają sensu z twojego punktu widzenia.
  3. Ten wybór jest najlepszy i można go wykonać RESTWspólnie. Jeśli tworzysz API i chcesz zezwolić na masowe zmiany zasobów, możesz użyć REST, aby to zrobić, ale dla wielu nie jest to od razu oczywiste. Jedną z metod jest utworzenie zasobu „żądanie zmiany” (np. Przez POST przesłanie treści takiej jak records=[1,2,3]to /delete-requests) i sondowanie utworzonego zasobu (określonego przez Locationnagłówek odpowiedzi), aby dowiedzieć się, czy żądanie zostało zaakceptowane, odrzucone, czy jest w toku lub zakończył. Jest to przydatne w przypadku długotrwałych operacji. Innym sposobem jest wysłanie PATCHzapytania do zasobu listy ,/records, którego treść zawiera listę zasobów i działań do wykonania na tych zasobach (w dowolnym formacie, który chcesz obsługiwać). Jest to przydatne w przypadku szybkich operacji, w których kod odpowiedzi na żądanie może wskazywać wynik operacji.

Wszystko można osiągnąć, trzymając się ograniczeń REST, a zwykle odpowiedzią jest przekształcenie „problemu” w zasób i nadanie mu adresu URL.
Tak więc operacje wsadowe, takie jak usuwanie w tym miejscu, wysyłanie wielu elementów do listy lub dokonywanie tej samej edycji zbioru zasobów, mogą być obsługiwane przez utworzenie listy „operacji wsadowych” i przesłanie do niej nowej operacji.

Nie zapominaj, że REST nie jest jedynym sposobem rozwiązania każdego problemu. „REST” to tylko styl architektoniczny i nie musisz się do niego stosować (ale jeśli tego nie zrobisz, stracisz pewne zalety internetu). Proponuję przejrzeć listę architektur HTTP API i wybrać tę, która Ci odpowiada. Po prostu uświadom sobie, na czym tracisz, wybierając inną architekturę, i podejmij świadomą decyzję w oparciu o swój przypadek użycia.

Czy istnieją złe odpowiedzi na to pytanie dotyczące wzorców do obsługi operacji wsadowych w usługach sieci Web REST? które mają o wiele za dużo głosów za, ale też powinny być przeczytane.

Nicholas Shanks
źródło
2
To nie serwer, o który musisz się martwić, to pośrednicy, sieci CDN, serwery proxy pamięci podręcznej itp. Internet jest systemem warstwowym. To jest powód, dla którego działa tak dobrze. Roy określił, które aspekty systemu są niezbędne do jego sukcesu i nazwał je REST. Jeśli wyślesz DELETEżądanie, cokolwiek znajduje się między odbiorcą żądania a serwerem, pomyśli, że pojedynczy zasób pod określonym adresem URL jest usuwany. Ciągi zapytań to nieprzezroczyste części adresu URL do tych urządzeń, więc nie ma znaczenia, w jaki sposób określisz swój interfejs API, nie są one wtajemniczone w tę wiedzę, więc nie mogą zachowywać się inaczej.
Nicholas Shanks,
3
/ records / 1; 2; 3 nie będzie działać, jeśli masz dużo zasobów do usunięcia z powodu ograniczeń długości identyfikatorów URI
dukethrash
3
Zwróć uwagę, że rozważając DELETE i organ definiujący zasoby do oczyszczenia, niektórzy pośrednicy mogą nie przekazywać treści. Ponadto niektórzy klienci HTTP nie mogą dodać treści do DELETE. Zobacz stackoverflow.com/questions/299628/…
Luke Puplett
3
@LukePuplett Powiedziałbym po prostu, że przekazywanie treści żądania z DELETEżądaniem jest zabronione. Nie rób tego. Jeśli to zrobisz, zjem twoje dzieci. Nom nom.
Nicholas Shanks
3
Problem z argumentem za # 3 polega na tym, że pociąga za sobą taką samą karę jak kontrargument przeciwko # 2. Tworzenie zasobu w celu usunięcia nie jest czymś, z czym proxy nadrzędne będą wiedzieć, jak sobie radzić - ten sam kontrargument, który jest podnoszony przeciwko podejściu nr 2.
LB2
16

Jeśli GET /records?filteringCriteriazwraca tablicę wszystkich rekordów spełniających kryteria, DELETE /records?filteringCriteriamożna usunąć wszystkie takie rekordy.

W takim przypadku odpowiedź na twoje pytanie brzmiałaby DELETE /records?id=1&id=2&id=3.

Martin Ždila
źródło
1
Doszedłem również do tego wniosku: po prostu odwróć czasownik do tego, co chcesz zrobić. Nie rozumiem, jak to, co dzieje się w przypadku GET, nie idzie do DELETE.
Luke Puplett
9
GET /records?id=1&id=2&id=3nie nie znaczy „dostać trzy rekordy z identyfikatorami 1, 2 & 3”, to znaczy „uzyskać pojedynczy zasób URL ścieżki / dokumentacje? id = 1 & id = 2 & id = 3”, który być może jest to obraz rzepa, zwykły tekst dokument zawierający numer „42” w języku chińskim lub może nie istnieć.
Nicholas Shanks
Weź pod uwagę następujące kwestie: dwa kolejne żądania wysyłania /records?id=1i /records?id=2są wysyłane, a ich odpowiedzi są przechowywane w pamięci podręcznej przez jakiegoś pośrednika (np. Twoją przeglądarkę lub dostawcę usług internetowych). Jeśli internet wiedział, co rozumie przez to twoja aplikacja, to ma się rozumieć, że żądanie /records?id=1&id=2może zostać zwrócone przez pamięć podręczną po prostu przez połączenie (w jakiś sposób) dwóch wyników, które już ma, bez konieczności pytania do serwera pochodzenia. Ale to nie jest możliwe. /records?id=1&id=2może być nieprawidłowy (tylko 1 identyfikator dozwolony na żądanie) lub może zwrócić coś zupełnie innego (rzepa).
Nicholas Shanks
Jest to podstawowy problem z buforowaniem zasobów. Jeśli mój DBA zmienił stan bezpośrednio, pamięci podręczne są teraz niezsynchronizowane. Podajesz przykład 410 zwracany przez pośrednika, ale 410 dotyczy trwałego usunięcia, po USUNIĘCIU pamięć podręczna może wyczyścić miejsce dla tego adresu URL, ale nie wyśle ​​410 ani 404, ponieważ nie wie, czy DBA nie tylko od razu przywrócił zasób do źródła.
Luke Puplett
4
@NicholasShanks Naprawdę się nie zgadzam. Jeśli wyniki są buforowane, jest to wina serwera. A jeśli mówisz o projekcie interfejsu API, mam nadzieję, że to ty piszesz kod dla serwera. Niezależnie od tego, czy używasz, id[]=1&id[]=2czy id=1&id=2w ciągu zapytania do reprezentowania tablicy wartości, ten ciąg zapytania reprezentuje właśnie to. I myślę, że to niezwykle powszechne i dobre praktyki, aby ciąg zapytania reprezentował filtr. Poza tym, jeśli zezwalasz na usuwanie i aktualizacje, nie buforuj GETżądań. Jeśli to zrobisz, klienci będą mieć nieaktualny stan.
Joseph Nields,
10

Myślę, że Mozilla Storage Service SyncStorage API v1.5 to dobry sposób na usunięcie wielu rekordów za pomocą REST.

Usuwa całą kolekcję.

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

Usuwa wiele BSO z kolekcji za pomocą jednego żądania.

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

ids : usuwa BSO z kolekcji, których identyfikatory znajdują się na podanej liście oddzielonej przecinkami. Można podać maksymalnie 100 identyfikatorów.

Usuwa BSO w podanej lokalizacji.

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

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

bootsoon
źródło
To wydaje się jak dobre rozwiązanie. Myślę, że jeśli Mozilla uważa, że ​​to prawda, to tak musi być? Jedyne pytanie to obsługa błędów. Załóżmy, że pasują? Ids = 1, 2, 3, a identyfikator 3 nie istnieje, czy usuwasz 1 i 2, a następnie odpowiadasz 200, ponieważ żądający chce, aby 3 zniknęło, a go tam nie ma, więc to nie ma znaczenia? a co jeśli mają oni upoważnienie do usunięcia 1, ale nie 2 ... czy nie usuwasz niczego i odpowiadasz z błędem, czy też usuwasz to, co możesz i zostawiasz pozostałe ...
tempcke
Zazwyczaj zwracam pomyślną odpowiedź, ponieważ stan końcowy jest taki sam, niezależnie od tego. Upraszcza to również logikę na kliencie, ponieważ nie muszą już obsługiwać tego stanu błędu. Jeśli chodzi o przypadek autoryzacji, po prostu zawiodę całe żądanie ... ale tak naprawdę to zależy od twojego przypadku użycia.
Nathan Phetteplace
3

Wydaje się, że jest to szara strefa konwencji REST.

Tak, do tej pory trafiłem tylko na jeden przewodnik projektowania interfejsu API REST, który wspomina o operacjach wsadowych (takich jak usuwanie partii): przewodnik projektowania interfejsu API Google .

W tym przewodniku wspomina się o tworzeniu „niestandardowych” metod, które można powiązać za pośrednictwem zasobu za pomocą dwukropka, np https://service.name/v1/some/resource/name:customVerb. Wyraźnie wspomina o operacjach wsadowych jako przypadku użycia:

Metoda niestandardowa może być skojarzona z zasobem, kolekcją lub usługą. Może zająć dowolne żądanie i zwrócić dowolną odpowiedź, a także obsługuje żądanie i odpowiedź przesyłania strumieniowego. [...] Niestandardowe metody powinny używać czasownika HTTP POST, ponieważ ma on najbardziej elastyczną semantykę [...] W przypadku metod krytycznych dla wydajności może być przydatne dostarczenie niestandardowych metod wsadowych w celu zmniejszenia obciążenia na żądanie .

Możesz więc wykonać następujące czynności zgodnie z przewodnikiem Google API:

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

... aby usunąć kilka elementów z zasobów kolekcji.

B12Toaster
źródło
Czy realne rozwiązanie polega na tym, że lista elementów jest przekazywana za pośrednictwem tablicy sformatowanej w formacie JSON?
Daniele,
Tak, oczywiście. można POSTOWAĆ ładunek, w którym identyfikatory są wysyłane za pośrednictwem tablicy json.
B12Toaster
Ciekawe, że przewodnik Google API powiedział 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,w rozdziale Custom Method. Ale GET accounts.locations.batchGetAPI to metoda GET z body. To jest dziwne. developers.google.com/my-business/reference/rest/v4/…
鄭元傑
@ 鄭元傑 zgadzam się, wygląda trochę dziwnie na pierwszy rzut oka, ale jeśli przyjrzysz się uważnie, jest to w rzeczywistości POSTużywana metoda http i nazwana jest tylko metoda niestandardowa batchGet. Domyślam się, że Google robi to, aby (a) trzymać się swojej zasady, że wszystkie niestandardowe metody muszą być POST(zobacz moją odpowiedź) i (b) aby ułatwić ludziom umieszczanie „filtru” w treści, abyś nie musiał uciec lub zakodować filtr tak, jak w przypadku ciągów zapytań. minusem jest oczywiście to, że nie
da się
https://service.name/v1/some/resource/name:customVerbz definicji nie jest RESTful.
deamon
2

Pozwoliłem na hurtową wymianę kolekcji, np. Gdy PUT ~/people/123/shoesciało jest reprezentacją całej kolekcji.

Działa to w przypadku małych podrzędnych kolekcji elementów, w przypadku których klient chce przejrzeć elementy, a niektóre usunąć i dodać inne, a następnie zaktualizować serwer. Mogliby umieścić pustą kolekcję, aby usunąć wszystkie.

Oznaczałoby to, GET ~/people/123/shoes/9że nadal pozostawałby w pamięci podręcznej, mimo że PUT usunął go, ale jest to tylko problem z pamięcią podręczną i byłby problemem, gdyby inna osoba usunęła but.

Moje interfejsy API danych / systemów zawsze używają znaczników ETag, a nie czasów wygaśnięcia, więc serwer jest trafiany przy każdym żądaniu i wymagam poprawnych nagłówków wersji / współbieżności, aby zmutować dane. W przypadku interfejsów API, które są tylko do odczytu i dostosowane do wyświetlania / raportowania, używam czasów wygaśnięcia, aby zmniejszyć trafienia w źródle, np. Tabela wyników może być dobra przez 10 minut.

W przypadku znacznie większych kolekcji, takich jak ~/people , zwykle nie potrzebuję wielokrotnego usuwania, przypadek użycia nie pojawia się naturalnie, więc pojedyncze DELETE działa dobrze.

W przyszłości, z doświadczenia w budowaniu REST API i trafianiu na te same problemy i wymagania, takie jak audyt, byłbym skłonny używać tylko czasowników GET i POST oraz projektować wokół zdarzeń, np. POST zmiana adresu zdarzenia, chociaż podejrzewam, że Pojawi się z własnym zestawem problemów :)

Pozwoliłbym również programistom front-end na tworzenie własnych interfejsów API, które używają bardziej rygorystycznych interfejsów API zaplecza, ponieważ często istnieją praktyczne, ważne powody po stronie klienta, dla których nie lubią surowych projektów interfejsu API REST „zelotów Fielding”, a także ze względu na produktywność i przyczyny warstw pamięci podręcznej.

Luke Puplett
źródło
Uwielbiałem tę odpowiedź aż do przeczytania ostatniego zdania :) Nigdy nie widziałem przypadku użycia, w którym zastosowanie ścisłego REST miałoby netto szkodliwy wpływ. Oczywiście, może to spowodować napisanie większej ilości kodu na obu końcach, ale w rezultacie otrzymasz bezpieczniejszy, czystszy i mniej powiązany system.
Nicholas Shanks
Ha ha. To faktycznie stało się wzorem! Backend dla front-endu jest nazywany radarem technologii ThoughtWorks. Pozwala także na napisanie większej ilości logiki aplikacji, która byłaby kłopotliwa w, powiedzmy, JavaScript i oczywiście może być aktualizowana bez klienta, więc na przykład aktualizacja dla aplikacji na iOS.
Luke Puplett
Przeglądając pierwsze cztery trafienia z Google, wydaje się, że ta technika BFF może działać tylko wtedy, gdy klienci są pod twoją kontrolą . Deweloperzy klientów opracowują żądany interfejs API, mapując wywołania do interfejsów API mikrousług, które są prawdziwym zapleczem. Na tym diagramie: samnewman.io/patterns/architectural/bff/#bff umieściłbym linię „Perimeter” poniżej pól BFF - każde pudełko jest po prostu częścią klienta. Może nawet mieszkać poza centrum danych, w którym mieszczą się mikrousługi. Nie widzę też, jak REST nie dotyczy obu interfejsów (klienta / BFF i BFF / mikrousługi).
Nicholas Shanks
1
Tak, to dobra uwaga. Zwykle dzieje się tak, gdy masz na przykład mikrousługi do budowania zespołu i zespół tworzący aplikację kątową, a ten zespół programistów jest bardziej typu front-end, który nie lubi pracować z kilkoma małymi purystycznymi usługami. Chociaż nie widzę żadnego powodu, dla którego nie można by użyć tego samego wzorca do abstrakcji mikrousług i agregacji w bardziej użyteczną fasadę dla klientów, tak aby mikrousługi można było zmienić bez wpływu na elewację.
Luke Puplett
Punkt końcowy API powinien modelować potrzeby domeny i biznesu. Kod, aby rozwiązać te problemy i uniknąć nadmiernej inżynierii, aby przestrzegać surowych i często nieelastycznych specyfikacji. REST to nic innego jak wytyczne.
Victorio Berra,