Czy można częściowo zmienić kolekcję za pomocą PUT lub DELETE?

21

Mam kolekcję produktów w grupie produktów, np .:

product-groups/123/products
  1. Jeśli muszę dodać do kolekcji, czy mogę przekazać tylko niektóre produkty z PUT?

  2. Jeśli muszę usunąć niektóre produkty z kolekcji, czy mogę przesyłać dane filtru (tablicę identyfikatorów) za pomocą DELETE?

Jaki jest najlepszy sposób na wdrożenie funkcjonalności w duchu ReST?

Edycja: elementy to linki do oddzielnych jednostek, w zasadzie identyfikatory produktów.

użytkownik151851
źródło
Czy produkty w grupie produktów są oddzielnymi zasobami zarządzanymi gdzie indziej? Czy są tylko częścią kolekcji grup produktów? Jeśli są odrębne, czy produkty mogą należeć do wielu grup produktów?
Martijn Pieters
2
być może PATCH Ta specyfikacja definiuje nową metodę HTTP / 1.1 [RFC2616], PATCH, która jest stosowana do stosowania częściowych modyfikacji zasobu.
Esailija,
Produkt (ID) może należeć do kilku grup produktów.
user151851
Czy istnieje dobrze znany sposób (najlepsza praktyka), w jaki sposób PATCHOWAĆ, czyli dodawać lub usuwać produkty z kolekcji?
user151851
Podobne pytanie na SO stackoverflow.com/questions/411462/…
Luke Puplett

Odpowiedzi:

10

Zasadniczo masz jeden punkt końcowy, który reprezentuje całą kolekcję x :

/products

Powiedzmy, chcesz zaktualizować jeden produkt, dokonaniu PUT do /products/{id}. Jeśli chcesz częściowo zaktualizować pojedynczy produkt (nie aktualizując każdego pola), możesz również użyć PATCH do /products/{id}. To samo dotyczy usunięcia pojedynczego elementu ( DELETE to /products/{id}).

Jeśli chcesz kierować pojedynczą ressource, kwalifikujesz torem, który pojedynczy Ressource, chcesz zmodyfikować.

Jedynym działaniem, które łamie schemat, jest utworzenie zasobu. Podczas tworzenia zasobu kierujesz kolekcję jako całość, powiedz POST na /products.

To powiedziawszy, powinno być jasne, że cel operacji wpływających na kolekcję jako całość powinien trafić do odpowiedniego punktu końcowego gromadzenia.

Np. Chcesz odzyskać podzbiór produktów, które są czerwone, pytasz o to przez

GET do /products?colour=red.

Tak więc, jeśli chcesz usunąć wszystkie z nich, to DELETE /products?colour=red . Lub jeśli chcesz usunąć niektóre z produktów za pośrednictwem id, można DELETE /products?id=1&id=2&id=3 .

Co z masowym tworzeniem zasobów? POST swoją kolekcję [{...},{...},{...}]po prostu /products. To samo dotyczy PUT i PATCH .

To naprawdę proste.

Aby odpowiedzieć na pytania:

Jeśli muszę dodać do kolekcji, czy mogę przekazać tylko niektóre produkty z PUT?

To nie tylko OK, zachęcamy Cię do robienia tego w ten sposób.

Jeśli muszę usunąć niektóre produkty z kolekcji, czy mogę przesyłać dane filtru (tablicę identyfikatorów) za pomocą DELETE?

W porządku. Jak napisał Eneko Alonso, czasami zdarzają się operacje zbiorcze za pomocą punktów końcowych „kontrolera”, tj. POST służy do wyzwalania (złożonych) operacji.

Thomas Junk
źródło
2
PUT to operacja zamiany. Wywołanie PUT na punkcie końcowym kolekcji z „niektórymi produktami” powinno usunąć (w przypadku PO, usunąć relację z) dowolnym produktem, który nie jest uwzględniony na liście „niektórych produktów”. Chociaż można go użyć do dodawania elementów, powinien również usuwać elementy, które nie są (moim zdaniem) tym, czego oczekuje OP. Powinieneś odpowiednio zmienić swoją odpowiedź na pierwsze pytanie.
claytond
@claytond: Podejrzewam, że odpowiedź jest w porządku, o ile dokonana jest częściowa aktualizacja PATCHi pełna wymiana za pośrednictwem PUT.
9000
4
@ 9000. Oczywiście, ale odpowiedź brzmi obecnie: „Zachęcamy do… dodania do kolekcji… [poprzez] przekazanie tylko niektórych produktów z PUT”. To z pewnością niepoprawne. Zachęcony do POST. W stanie ZŁOŻYĆ ... ale tylko przekazując wszystkie (nie niektóre) przedmioty.
claytond
5

Zazwyczaj metody REST są przeznaczone do działania na pojedynczym obiekcie / obiekcie (CRUD).

Istnieje kilka opcji:

  • Traktuj swoje kolekcje jako byty i aktualizuj je za pomocą POST
  • Twórz alternatywne operacje bez REST

Pierwszy jest zgodny ze standardami REST, ale może być kosztowny, ponieważ obiekty / jednostki kolekcji mogą być bardzo duże (aktualizacja grupy, która ma tysiące produktów tylko w celu dodania / usunięcia jednego produktu, byłaby dużym żądaniem).

Druga opcja jest preferowana przez wiele interfejsów API, jako sposób na rozszerzenie REST poza operacje CRUD.

Na przykład:

GET product-groups/123/products (list all the products in the group)
POST product-groups/123/products/append (POST a list of new product ids to append to the group)
POST product-groups/123/products/remove (POST a list of product ids to remove from the group)

Wiele interfejsów API zawsze używa POST dla tych rozszerzonych operacji, ale nic nie ogranicza korzystania z innych metod http (innych niż ograniczenie GET i DELETE, aby mieć puste ciało)

Eneko Alonso
źródło
Jasne, istnieje kilka metod osiągnięcia celu. Która jest najlepszą praktyką? Który będzie bardziej przyszłościowy?
user151851
4
@ user151851: Całkowita zgodność z REST (jeśli coś takiego istnieje) jest wzniosłym celem. Zarys podejścia tutaj wydaje się bardziej realistyczny, ponieważ próbuje zastosować podejście, które jest faktycznie stosowane w „prawdziwym świecie”, co czyni go w istocie standardem defaktycznym. To będzie tak przyszłościowe, jak tylko się da.
Robert Harvey
2
Czy nie wprowadzamy niestandardowych czasowników za pomocą „append” i „delete” w adresie URL? W ten sposób będziemy musieli wyjaśnić, jak korzystać z interfejsu API. Czy nie powinniśmy ponownie wykorzystywać tego, co mamy, tj. Metod HTTP? W takim przypadku działania są dobrze znane.
user151851
7
Dla każdego, kto spotka się z tą odpowiedzią: to źle. Jak wspomniano @ user151851, wprowadza czasowniki do adresu URL, który jest mniej niż RESTful, jak możesz. Jeśli chodzi o aktualne pytanie, nie mam świetnej odpowiedzi, ale to nie wszystko.
umbrae
Czy „rozszerzenie” może być bardziej zorientowane na zasoby, ponieważ products/collectionzwraca „kopertę” elementów, a zawartość koperty zmienia się za pomocą PUT? Na przykład „oto dokładnie, jak chcę, aby elementy w kolekcji były”.
Luke Puplett
3

Dokładnie poprzednie odpowiedzi / komentarze.

Zgodnie z moją wiedzą POST jest metodą dodawania pojedynczych elementów do kolekcji.

Z kolei DELETE to metoda usuwania pojedynczego elementu z kolekcji. Oba scenariusze są całkowicie PRZYWRACANE.

Należy jednak użyć odpowiedniego identyfikatora URI, aby odnieść się do pojedynczego elementu lub całej kolekcji.

Na przykład, aby dodać element do kolekcji, należy POST dane do następującego identyfikatora URI:

https://www.factory.net/products/

Aby usunąć pojedynczy produkt z kolekcji, możesz użyć metody DELETE wysyłającej żądanie do czegoś takiego:

https://www.factory.net/products/108/

Za pomocą metody PATCH można zaktualizować niektóre elementy w kolekcji. Na przykład, gdy musisz zaktualizować tylko jedno pole w jednym elemencie. Umieszczenie pełnej reprezentacji zasobów dla bardzo dużej kolekcji może być bardzo kosztownym działaniem.

Łukasz
źródło
2

Zasadniczo wszystkie operacje RESTful są poprawne dla kolekcji, ale upewnij się, że rozumiesz, jak semantyka czasowników ma zastosowanie do kolekcji:

  • PUT jest kompletnym zamiennikiem.

    • Jeśli umieścisz w singletonie (np. /item/{id}) I pominiesz name, należy go wyczyścić lub ustawić na zero lub coś podobnego.
    • Jeśli umieszczasz w kolekcji i nie zawierasz elementu, należy go usunąć z tej kolekcji.

    Chociaż do dodania elementów można użyć PUT, musisz wysłać „wszystkie” elementy. Wysłanie „niektórych” elementów powinno spowodować usunięcie (zakładam, że nie tego chce OP).

  • DELETE jest bardziej intuicyjny. Poprawne jest usunięcie kolekcji lub dowolnego jej odfiltrowanego podzbioru. Wpływa to tylko na elementy zawarte w filtrze.

  • PATCH jest również ważny. Teoretycznie powinieneś podać listę „operacji”. Na przykład powinieneś technicznie wysłać coś takiego:

    [{ 
        "action": "update",
        "id": <id>,
        "value": {...}
    },{
        "action": "add",
        "value": {...}
    }, ...]
    

    W praktyce częściej występuje interfejs API, który akceptuje częściową listę obiektów, w których każdy element jest przetwarzany za pomocą logiki UPSERT (aktualizacja lub wstawianie).

  • Technicznie, test POST powinien przetwarzać dane wejściowe „zgodnie z własną semantyką zasobu”.

    • W praktyce POST jest zwykle używany do operacji „tworzenia”.
    • Jednak POST jest także czasownikiem używanym do niestandardowych połączeń. Chociaż toczy się energiczna debata na temat tego, czy punkty końcowe działania są RESTfully (ja jestem po stronie „nie”), POST jest odpowiednim czasownikiem, jeśli składasz żądanie do punktu końcowego, takiego jak {resource}/activate.

UWAGA: Podczas korzystania z operacji innych niż GET na kolekcjach, dokładnie rozważ definicję sukcesu i niepowodzenia. Usługa REST nie zapewnia dobrego sposobu komunikowania częściowego sukcesu. Dobrym rozwiązaniem domyślnym jest założenie, że operacja zostanie przeprowadzona w transakcji spełniającej kryteria sukcesu „wszystko albo nic”. Jeśli nie tego chcesz, prawdopodobnie nie powinieneś bezpośrednio wchodzić w interakcje z kolekcją.

Claytond
źródło