Zasoby złożone / złożone / zagnieżdżone REST [zamknięte]

177

Próbuję omówić najlepszy sposób rozwiązywania koncepcji w interfejsie API opartym na REST. Płaskie zasoby, które nie zawierają innych zasobów, nie stanowią problemu. Tam, gdzie mam kłopoty, są złożone zasoby.

Na przykład mam zasób do komiksu. ComicBookma wszystkie rodzaje nieruchomości na nim jak author, issue number, date, itd.

Komiks zawiera również listę 1..nokładek. Te osłony są złożonymi obiektami. Zawierają wiele informacji o okładce: artystę, datę, a nawet obraz okładki zakodowany w bazie 64.

Dla GETna ComicBookI może po prostu wrócić komiks, a wszystkie okładki tym ich base64'ed obrazów. To prawdopodobnie nie jest wielka sprawa, jeśli chodzi o zdobycie jednego komiksu. Ale przypuśćmy, że tworzę aplikację kliencką, która chce wyświetlić listę wszystkich komiksów w systemie w tabeli.
Tabela będzie zawierać kilka właściwości z ComicBookzasobu, ale z pewnością nie będziemy chcieli wyświetlać wszystkich okładek w tabeli. Zwrócenie 1000 komiksów, każdy z wieloma okładkami, spowodowałoby śmiesznie dużą ilość danych, które w tym przypadku nie są konieczne dla użytkownika końcowego.

Moim instynktem jest tworzenie Coverzasobów i posiadanie ComicBookokładek. Więc teraz Coverjest URI. GETna komiksie działa teraz, zamiast ogromnych Coverzasobów wysyłamy z powrotem identyfikator URI dla każdej okładki, a klienci mogą pobierać zasoby z okładki, gdy ich potrzebują.

Teraz mam problem z tworzeniem nowych komiksów. Z pewnością będę chciał stworzyć co najmniej jedną okładkę, kiedy tworzę Comic, w rzeczywistości jest to prawdopodobnie zasada biznesowa.
Więc teraz utknąłem, ja albo zmusić klientów do egzekwowania reguł biznesowych najpierw złożenie Cover, coraz URI dla tej pokrywy, a następnie POSTing ComicBookz tym URI z listy, albo mój POSTna ComicBookodbywa się w innym zasobie patrząc niż pluje na zewnątrz. Przychodzące zasoby POSTi GETsą kopiami głębokimi, podczas gdy wychodzące GETzawierają odniesienia do zasobów zależnych.

CoverZasób jest prawdopodobnie konieczne w każdym przypadku, ponieważ jestem pewien, że jako klient chciałbym do kierunku pokryw adres w niektórych przypadkach. Zatem problem występuje w ogólnej postaci, niezależnie od wielkości zależnego zasobu. Jak ogólnie radzisz sobie ze złożonymi zasobami bez zmuszania klienta do „wiedzy”, jak te zasoby są zbudowane?

jgerman
źródło
czy korzystanie z RESTFUL SERVICE DISCOVERY ma sens?
treecoder
1
Staram się trzymać HATEAOS, co moim zdaniem jest sprzeczne z używaniem czegoś takiego, ale przyjrzę się temu.
jgerman
Inne pytanie w tym samym duchu. Jednak własność różni się od proponowanego rozwiązania (tego, którego dotyczy pytanie). stackoverflow.com/questions/20951419/…
Wes

Odpowiedzi:

64

@ray, doskonała dyskusja

@jgerman, nie zapominaj, że tylko dlatego, że jest to REST, nie oznacza, że ​​zasoby muszą być osadzone w kamieniu z POST.

To, co zdecydujesz się uwzględnić w dowolnej reprezentacji zasobu, zależy od Ciebie.

Twój przypadek okładek, do których odwołuje się osobno, jest jedynie stworzeniem zasobu nadrzędnego (komiksu), do którego zasobów podrzędnych (okładek) można się odwoływać. Na przykład możesz również chcieć osobno podać odniesienia do autorów, wydawców, postaci lub kategorii. Możesz chcieć utworzyć te zasoby osobno lub przed komiksem, który odnosi się do nich jako zasobów podrzędnych. Alternatywnie możesz chcieć utworzyć nowe zasoby podrzędne podczas tworzenia zasobu nadrzędnego.

Twój konkretny przypadek okładek jest nieco bardziej złożony, ponieważ okładka naprawdę wymaga komiksu i na odwrót.

Jeśli jednak uznasz wiadomość e-mail za zasób, a adres nadawcy jako zasób podrzędny, możesz oczywiście nadal odwoływać się do adresu nadawcy osobno. Na przykład pobierz wszystko z adresów. Lub utwórz nową wiadomość z poprzednim adresem nadawcy. Gdyby poczta e-mail była typu REST, można z łatwością zauważyć, że dostępnych jest wiele powiązanych zasobów: / odebrane-wiadomości, / robocze wiadomości, / od-adresy, / na-adresy, / adresy, / tematy, / załączniki, / foldery , / tags, / category, / labels, et al.

Ten samouczek stanowi doskonały przykład zasobów, do których istnieją odsyłacze. http://www.peej.co.uk/articles/restfully-delicious.html

Jest to najczęstszy wzorzec dla danych generowanych automatycznie. Na przykład nie publikujesz identyfikatora URI, identyfikatora ani daty utworzenia nowego zasobu, ponieważ są one generowane przez serwer. Mimo to możesz odzyskać identyfikator URI, identyfikator lub datę utworzenia po odzyskaniu nowego zasobu.

Przykład w Twoim przypadku danych binarnych. Na przykład chcesz opublikować dane binarne jako zasoby podrzędne. Gdy otrzymasz zasób nadrzędny, możesz przedstawić te zasoby podrzędne jako te same dane binarne lub jako identyfikatory URI, które reprezentują dane binarne.

Formularze i parametry są już inne niż reprezentacje HTML zasobów. Publikowanie parametru binarnego / pliku, którego wynikiem jest adres URL, nie jest rozciągnięte.

Kiedy otrzymujesz formularz dla nowego zasobu (/ comic-book / new) lub otrzymujesz formularz do edycji zasobu (/ comic-book / 0 / edit), prosisz o specyficzną dla formularza reprezentację zasobu. Jeśli wyślesz go do kolekcji zasobów z typem zawartości „application / x-www-form-urlencoded” lub „multipart / form-data”, poprosisz serwer o zapisanie reprezentacji tego typu. Serwer może odpowiedzieć zapisaną reprezentacją HTML lub jakąkolwiek inną.

Możesz również zezwolić na wysłanie reprezentacji HTML, XML lub JSON do kolekcji zasobów, do celów API lub podobnych.

Możliwe jest również przedstawienie zasobów i przepływu pracy zgodnie z opisem, biorąc pod uwagę okładki opublikowane po komiksie, ale wymagające, aby komiksy miały okładkę. Przykład w następujący sposób.

  • Umożliwia opóźnione tworzenie okładek
  • Umożliwia tworzenie komiksów z wymaganą okładką
  • Umożliwia odsyłacze do okładek
  • Umożliwia wiele osłon
  • Utwórz szkic komiksu
  • Twórz projekty okładek komiksów
  • Opublikuj szkic komiksu

GET / comic-books
=> 200 OK, pobierz wszystkie komiksy.

GET / comic-books / 0
=> 200 OK, Pobierz komiks (id: 0) z okładkami (/ okładki / 1, / okładki / 2).

GET / comic-book / 0 / cover
=> 200 OK, Zdobądź okładki do komiksu (id: 0).

GET / cover
=> 200 OK, pobierz wszystkie okładki.

GET / cover / 1
=> 200 OK, zdobądź okładkę (id: 1) z komiksem (/ comic-book / 0).

GET / comic-books / new
=> 200 OK, Uzyskaj formularz do stworzenia komiksu (formularz: POST / draft-comic-book).

POST / draft-comic-book
title = foo
author = boo
publisher = goo Published
= 2011-01-01
=> 302 Znaleziono, lokalizacja: / draft-comic-books / 3, Przekierowanie do wersji roboczej komiksu (id: 3) z okładki (binarne).

GET / draft-comic-books / 3
=> 200 OK, pobierz szkic komiksu (id: 3) z okładkami.

GET / draft-comic-books / 3 / cover
=> 200 OK, Zdobądź okładki do szkicu komiksu (/ draft-comic-book / 3).

GET / draft-comic-books / 3 / cover / new
=> 200 OK, Uzyskaj formularz tworzenia okładki szkicu komiksu (/ draft-comic-book / 3) (formularz: POST / draft-comic-books / 3 / okładki).

POST / draft-comic-books / 3 /
cover_type = front
cover_data = (binary)
=> 302 Znalezione, Lokalizacja: / draft-comic-books / 3 / covery, Przekierowanie do nowej okładki szkicu komiksu (/ draft-comic -book / 3 / okładki / 1).

GET / draft-comic-books / 3 / publisher
=> 200 OK, Pobierz formularz do opublikowania wersji roboczej komiksu (id: 3) (formularz: POST / Published-comic-books).

POST / opublikowany-
tytuł -komiksu = foo
autor = boo
wydawca = goo
opublikowany = 2011-01-01
cover_type = front
cover_data = (binary)
=> 302 Znaleziono, lokalizacja: / comic-books / 3, przekierowanie do opublikowanego komiksu (id: 3) z okładkami.

Alex
źródło
Jestem całkowitym nowicjuszem w tej dziedzinie i próbuję się tego nauczyć w pośpiechu. Uznałem to za niezwykle pomocne. Jednak na innych blogach itp., Które czytałem dzisiaj, użycie GET do wykonania operacji (szczególnie operacji, która nie jest idempotentna) byłoby źle widziane. Czy więc nie powinno to być POST / szkic-komiksów / 3 / publikacja?
Gary McGill
3
@GaryMcGill W jego przykładzie / draft-comic-books / 3 / publikacja zwraca tylko formularz HTML (nie modyfikuje żadnych danych).
Olivier Lalonde
@Olivier ma rację. Słowo publikuj ma na celu określenie, co robi formularz. Jednakże, ponieważ chcesz, aby czasowniki były ograniczone do metod HTTP, powinieneś publikować w zasobach z publikowanymi komiksami. ... Gdyby to była witryna internetowa, może być potrzebny identyfikator URI, aby formularz mógł coś opublikować. ... Chociaż, gdyby czynnością publikowania był tylko jeden przycisk na stronie komiksu, ten jednoprzyciskowy formularz mógłby wysłać wiadomość bezpośrednio do URI / Published-comic-books.
Alex,
@Alex, w żądaniu POST zwróciłbym zamiast tego 201 Created, z adresem URL nowego zasobu jako Location w nagłówkach odpowiedzi.
ismriv
2
@Stephane, przekierowania po prostu upraszczają wszystko dla kontrolerów. Nawet w przypadku interfejsu API prościej jest, gdy kontroler tworzenia zwraca lokalizację nowej zawartości, a następnie pozwala kontrolerowi show obsługiwać wyświetlanie nowej zawartości. Chociaż dla klienta API przyjemniej / prościej jest po prostu pobrać zawartość i nie przejmować się przekierowaniami.
Alex
45

Traktowanie okładek jako zasobów jest zdecydowanie w duchu REST, zwłaszcza HATEOAS. Więc tak, GETprośba o http://example.com/comic-books/1przekazanie reprezentacji książki 1, z właściwościami obejmującymi zestaw identyfikatorów URI okładek. Na razie w porządku.

Twoje pytanie brzmi, jak radzić sobie z tworzeniem komiksów. Jeśli Twoja reguła biznesowa była taka, że ​​książka miałaby 0 lub więcej okładek, nie masz problemu:

POST http://example.com/comic-books

z danymi komiksu bez okładki utworzy nowy komiks i zwróci identyfikator wygenerowany przez serwer (powiedzmy, że wróci jako 8), a teraz możesz dodać do niego okładki w następujący sposób:

POST http://example.com/comic-books/8/covers

z okładką w korpusie jednostki.

Teraz masz dobre pytanie, co się dzieje, jeśli Twoja reguła biznesowa mówi, że zawsze musi być co najmniej jedna okładka. Oto kilka opcji, z których pierwszą wskazałeś w swoim pytaniu:

  1. Wymuś najpierw utworzenie okładki, teraz zasadniczo czyniąc okładkę zasobem niezależnym lub umieszczasz początkową okładkę w treści jednostki POST, która tworzy komiks. To, jak mówisz, oznacza, że ​​reprezentacja, którą POST utworzysz, będzie się różnić od reprezentacji, którą OTRZYMASZ.

  2. Zdefiniuj pojęcie pokrycia podstawowego, początkowego, preferowanego lub w inny sposób wyznaczonego. Jest to prawdopodobnie hack dotyczący modelowania, a jeśli to zrobiłeś, byłoby to jak modyfikowanie modelu obiektowego (modelu koncepcyjnego lub biznesowego) w celu dopasowania do technologii. Niezbyt dobry pomysł.

Powinieneś rozważyć te dwie opcje, a nie zezwalanie na komiksy bez okładki.

Którą z trzech opcji powinieneś wybrać? Nie wiedząc zbyt wiele o twojej sytuacji, ale odpowiedz na ogólne pytanie dotyczące zasobów zależnych 1..N, powiedziałbym:

  • Jeśli możesz wybrać 0..N dla warstwy usług RESTful, to świetnie. Być może warstwa między RESTful SOA poradzi sobie z dalszymi ograniczeniami biznesowymi, jeśli przynajmniej jedno jest wymagane. (Nie jestem pewien, jak to by wyglądało, ale warto to zbadać ... użytkownicy końcowi i tak zwykle nie widzą SOA).

  • Jeśli po prostu musisz modelować ograniczenie 1..N, zadaj sobie pytanie, czy okładki mogą być po prostu zasobami, które można udostępniać, innymi słowy, mogą istnieć na rzeczach innych niż komiksy. Teraz nie są to zasoby zależne i możesz je najpierw utworzyć i podać URI w swoim POST, który tworzy komiksy.

  • Jeśli potrzebujesz 1..N, a okładki pozostają zależne, po prostu rozluźnij swój instynkt, aby zachować te same reprezentacje w POST i GET lub uczynić je takimi samymi.

Ostatni punkt jest wyjaśniony w następujący sposób:

<comic-book>
  <name>...</name>
  <edition>...</edition>
  <cover-image>...BASE64...</cover-image>
  <cover-image>...BASE64...</cover-image>
  <cover>...URI...</cover>
  <cover>...URI...</cover>
</comic-book>

Podczas POST zezwalasz na istniejące uris, jeśli je masz (pożyczone z innych książek), ale także umieszczasz jeden lub więcej początkowych obrazów. Jeśli tworzysz książkę, a Twój podmiot nie ma początkowego obrazu okładki, zwróć odpowiedź 409 lub podobną. Na GET możesz zwrócić identyfikatory URI.

Tak więc zasadniczo pozwalasz, aby reprezentacje POST i GET były „takie same”, ale po prostu decydujesz się nie „używać” okładki w GET ani okładki w POST. Mam nadzieję, że to ma sens.

Ray Toal
źródło