Jaki jest najlepszy wzorzec dodawania istniejącego elementu do kolekcji w interfejsie API REST?

23

Projektuję pragmatyczny interfejs API REST i trochę utknąłem na tym, jak najlepiej dodawać istniejące elementy do kolekcji. Mój model domeny obejmuje projekt, który ma zbiór witryn. Jest to ścisła relacja wiele do wielu i nie muszę tworzyć encji, która jawnie modeluje relację (tj. ProjectSite).

Mój interfejs API pozwoli konsumentom dodać istniejącą witrynę do projektu. Rozłącza mnie to, że jedyne dane, których naprawdę potrzebuję, to ProjectId i SiteId. Mój początkowy pomysł brzmiał:

1. POST myapi/projects/{projectId}/sites/{siteId}

Ale myślałem również

2. POST myapi/projects/{projectId}/sites

z jednostką witryny wysłaną jako treść JSON.

Opcja 1 jest prosta i działa, ale nie wydaje się całkiem właściwa, i mam inne relacje, które nie mogą podążać za tym wzorem, więc dodaje niespójności do mojego interfejsu API.

Opcja 2 wydaje się lepsza, ale prowadzi do dwóch obaw:

  • Czy powinienem utworzyć witrynę, czy zgłosić wyjątek, jeśli zostanie opublikowana nowa witryna (SiteId = 0)?
  • Ponieważ do utworzenia relacji potrzebuję tylko ProjectId i SiteId, witryna może zostać opublikowana z błędnymi lub brakującymi danymi dla innych właściwości.

Trzecią opcją jest zapewnienie prostego punktu końcowego wyłącznie do tworzenia i usuwania relacji. Ten punkt końcowy oczekiwałby ładunku JSON zawierającego tylko ProjectId i SiteId.

Co myślisz?

Jamie Ide
źródło
2
Zobacz także: stackoverflow.com/questions/2001773/…
Rory Hunter
@RoryHunter W tym linku jest kilka interesujących dyskusji, ale nic nie eliminuje mojej niepewności. Szczególnie podoba mi się, że zaakceptowana odpowiedź brzmi „Dobrze zrozumiałeś”. i drugie miejsce (aczkolwiek z dużym marginesem) odpowiedź „Mówiąc wprost, robisz to całkowicie wstecz”.
Jamie Ide,
Twoja pierwsza opcja jest w porządku, ale użyłbym PUT zamiast POST, ponieważ klient kontroluje tożsamość dodawaną do kolekcji. Twój pierwszy problem z opcją 2 zależy wyłącznie od Ciebie, jeśli nie chcesz nowych witryn, nie zgłaszaj wyjątku, ale zwróć jeden z kodów 4xx. Twój drugi problem nie jest ani tu, ani tam. Nie powinieneś publikować całej witryny, chyba że zezwalasz na dodawanie. Dodanie istniejącej witryny powinno mieć identyfikator tylko podczas modyfikowania witryny, ale tylko kolekcję „ProjectSite” (nawet jeśli nie utworzysz dla niej osobnego zasobu).
Marjan Venema

Odpowiedzi:

14

POST jest czasownikiem „dołączającym”, a także czasownikiem „przetwarzającym”. PUT jest czasownikiem „twórz / aktualizuj” (dla znanych identyfikatorów) i prawie wygląda tutaj właściwy wybór, ponieważ znany jest pełny docelowy identyfikator URI. projectIdi siteIdjuż istnieją, więc nie trzeba „POST do kolekcji”, aby utworzyć nowy identyfikator.

Problem z PUT polega na tym, że ciało musi reprezentować zasób, który umieszczasz. Ale tutaj chodzi o dołączenie do zasobu kolekcji „projekt / witryny”, a nie o aktualizację zasobu Witryna.

Co się stanie, jeśli ktoś UMIESZCZY pełną reprezentację JSON istniejącej witryny? Czy należy zaktualizować kolekcję i obiekt? Możesz to wesprzeć, ale wygląda na to, że nie o to chodzi. Jak powiedziałeś,

jedyne potrzebne mi dane to ProjectId i SiteId

Zamiast tego postaram się wysłać test POST siteIddo kolekcji i polegać na „dołączaniu” i „przetwarzaniu” natury testu POST:

POST myapi / projects / {projectId} / sites

{'ID': '...' }

Ponieważ modyfikujesz zasób zbioru witryn, a nie zasób witryny , to jest to odpowiedni identyfikator URI. POST potrafi „dołączyć / przetworzyć” i dodać element o tym identyfikatorze do zbioru witryn projektu.

To wciąż pozostawia drzwi do tworzenia zupełnie nowych stron dla projektu poprzez rozwinięcie JSON i pominięcie identyfikatora. „Brak identyfikatora” == „Utwórz od zera”. Ale jeśli identyfikator URI kolekcji otrzyma identyfikator i nic więcej, jasne jest, co musi się stać.

Interesujące pytanie. :)

Obrabować
źródło
Jestem przekonany, że wierzy, że POST jest do tworzenia, a PUT do aktualizacji, ale twój wniosek jest tam, gdzie skończyłem wczoraj. Zaletą jest to, że dzięki routingowi atrybutów w Web API mam kod w kontrolerze ProjectSites, dzięki czemu kod jest dobrze zorganizowany.
Jamie Ide,
Myślę, że głównym powodem, dla którego musisz użyć POSTzamiast PUTlub PATCHtutaj jest to, że nie masz całej Siteencji do umieszczenia w siteszasobie. Masz tylko identyfikator, który wymaga przetworzenia, aby dodać go do kolekcji.
zmiażdżyć
4

Używamy tej Patchmetody do takich rzeczy. Co chcesz zrobić, to zmodyfikować istniejący projekt, aby dodać do niego witrynę.

Więc coś takiego działałoby

PATCH myapi/projects/{id} 

z jednostką Witryny jako JSON / JSONArray w treści żądania.

W ten sposób możesz użyć tego samego adresu URL, aby w razie potrzeby zmodyfikować różne części projektu - kod w implementacji musi być wystarczająco inteligentny, aby poradzić sobie z tą częściową modyfikacją zasobu.

juan
źródło
Ciekawe podejście Mam „bogaty” (tzn. Wysoce zależny) starszy model domeny, a Project ma szczególnie wiele kolekcji. Wykrywanie typu bytu znajdującego się w żądaniu byłoby wyzwaniem i nie pasuje do mojego pragmatycznego celu.
Jamie Ide,
Dlaczego wyzwanie? Jeśli masz te ograniczenia, zawsze możesz użyć JSON, który wyraźnie określa, co wysyła ... na przykład {"sites": [], "other-stuff": {}}, możesz następnie rozgałęzić swój kod, aby bardzo łatwo obsługiwać wszystkie te „podwiązki”. To naprawdę zależy od konkretnego problemu, ale nadal polecam użycie PATCH, ponieważ jest on zaprojektowany specjalnie do tego rodzaju rzeczy.
juan
Wady, które widzę to: 1) API nie komunikuje jawnie, które kolekcje zezwalają na zmiany; 2) nie może wykorzystać wiązania parametrów interfejsu API sieci Web; 3) duży przełącznik lub instrukcja if.
Jamie Ide
Nigdy nie widziałem metody łaty stosowanej nigdzie indziej
NimChimpsky
Czy nie PATCHspodziewałby się również, że pełny byt zostanie tutaj przekazany jako jego wartość, a nie identyfikator wskazujący na jakiś byt?
zmiażdżyć