Mam hierarchię obiektów, którą muszę ujawnić za pośrednictwem interfejsu API RESTful i nie jestem pewien, jak powinny być zbudowane moje adresy URL i co powinny zwracać. Nie udało mi się znaleźć żadnych najlepszych praktyk.
Powiedzmy, że mam psy i koty dziedziczące po zwierzętach. Potrzebuję operacji CRUD na psach i kotach; Chcę też mieć możliwość wykonywania ogólnych operacji na zwierzętach.
Moim pierwszym pomysłem było zrobienie czegoś takiego:
GET /animals # get all animals
POST /animals # create a dog or cat
GET /animals/123 # get animal 123
Chodzi o to, że kolekcja / animals jest teraz „niespójna”, ponieważ może zwracać i przyjmować obiekty, które nie mają dokładnie tej samej struktury (psy i koty). Czy kolekcję zwracającą obiekty o różnych atrybutach uważa się za „RESTful”?
Innym rozwiązaniem byłoby utworzenie adresu URL dla każdego konkretnego typu, na przykład:
GET /dogs # get all dogs
POST /dogs # create a dog
GET /dogs/123 # get dog 123
GET /cats # get all cats
POST /cats # create a cat
GET /cats/123 # get cat 123
Ale teraz związek między psami i kotami jest stracony. Jeśli ktoś chce odzyskać wszystkie zwierzęta, należy zapytać zarówno o zasoby psa, jak i kota. Liczba adresów URL również będzie wzrastać z każdym nowym podtypem zwierzęcia.
Inną sugestią było rozszerzenie drugiego rozwiązania poprzez dodanie tego:
GET /animals # get common attributes of all animals
W takim przypadku zwrócone zwierzęta będą zawierały tylko atrybuty wspólne dla wszystkich zwierząt, z pominięciem atrybutów specyficznych dla psów i kotów. Pozwala to na odzyskanie wszystkich zwierząt, choć z mniejszą liczbą szczegółów. Każdy zwracany obiekt może zawierać link do szczegółowej, konkretnej wersji.
Jakieś uwagi lub sugestie?
źródło
GET /animals - gets all dogs and cats
GET /animals/dogs - gets all dogs
GET /animals/cats - gets all cats
GET /animals
Akceptujapplication/vnd.vet-services.animal.dog+json
POST
operacji, ponieważ większość frameworków nie wiedziałaby, jak prawidłowo deserializować go do modelu, ponieważ json nie zawiera dobrych informacji o wpisywaniu. Jak radziłbyś sobie np. Ze sprawami pocztowymi[{"type":"dog","name":"Fido","playsFetch":true},{"type":"cat","name":"Sparkles","likesToPurr":"sometimes"}
?Lepszą odpowiedź na to pytanie można uzyskać, korzystając z niedawnego ulepszenia wprowadzonego w najnowszej wersji OpenAPI.
Możliwe było łączenie schematów za pomocą słów kluczowych, takich jak oneOf, allOf, anyOf i uzyskiwanie ładunku wiadomości zweryfikowanego od wersji 1.0 schematu JSON.
https://spacetelescope.github.io/understanding-json-schema/reference/combining.html
Jednak w OpenAPI (dawniej Swagger) skład schematów został ulepszony przez słowa kluczowe dyskryminator (v2.0 +) i oneOf (v3.0 +), aby naprawdę wspierać polimorfizm.
https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#schemaComposition
Twoje dziedzictwo może być modelowane przy użyciu kombinacji oneOf (w celu wybrania jednego z podtypów) i allOf (w celu połączenia typu i jednego z jego podtypów). Poniżej znajduje się przykładowa definicja metody POST.
źródło
Poszedłbym po / zwierzęta zwracając listę zarówno psów, jak i ryb i co jeszcze:
Wdrożenie podobnego przykładu JSON powinno być łatwe.
Klienci zawsze mogą polegać na obecnym elemencie „name” (wspólnym atrybucie). Ale w zależności od atrybutu „typ” będą inne elementy jako część reprezentacji zwierzęcia.
W zwracaniu takiej listy nie ma nic z natury RESTful ani unRESTful - REST nie określa żadnego konkretnego formatu reprezentacji danych. Mówi się tylko, że dane muszą mieć jakąś reprezentację, a format tej reprezentacji jest identyfikowany przez typ nośnika (który w HTTP jest nagłówkiem Content-Type).
Pomyśl o swoich przypadkach użycia - czy musisz pokazać listę zwierząt mieszanych? W takim razie zwróć listę mieszanych danych o zwierzętach. Czy potrzebujesz tylko listy psów? Cóż, zrób taką listę.
Czy robisz / animals? Type = dog czy / dogs nie ma znaczenia w odniesieniu do REST, który nie narzuca żadnych formatów URL - to jest pozostawione jako szczegół implementacji poza zakresem REST. REST tylko stwierdza, że zasoby powinny mieć identyfikatory - nieważne w jakim formacie.
Aby zbliżyć się do interfejsu API RESTful, należy dodać kilka hiperłączy. Na przykład dodając odniesienia do szczegółów dotyczących zwierząt:
Dodając hiper media link, zmniejszasz sprzężenie klient / serwer - w powyższym przypadku zdejmujesz ciężar budowy URL z klienta i pozwalasz serwerowi decydować o tym, jak konstruować adresy URL (które z definicji jest jedynym autorytetem).
źródło
Rzeczywiście, ale pamiętaj, że URI po prostu nigdy nie odzwierciedla relacji między obiektami.
źródło
Wiem, że to stare pytanie, ale jestem zainteresowany zbadaniem dalszych problemów dotyczących modelowania dziedziczenia RESTful
Zawsze mogę powiedzieć, że pies to zwierzę i kura też, ale kura robi jaja, a pies to ssak, więc nie może. API jak
POBIERZ zwierzęta /: identyfikator zwierzęcia / jaja
nie jest spójny, ponieważ wskazuje, że wszystkie podtypy zwierząt mogą mieć jaja (w wyniku substytucji Liskova). Nastąpiłby błąd, gdyby wszystkie ssaki odpowiedziały na to żądanie „0”, ale co, jeśli włączę również metodę POST? Czy mam się obawiać, że jutro w moich naleśnikach będą jaja psa?
Jedynym sposobem na poradzenie sobie z tymi scenariuszami jest zapewnienie `` superzasobu '', który agreguje wszystkie zasoby współdzielone między wszystkimi możliwymi `` zasobami pochodnymi '', a następnie specjalizację dla każdego zasobu pochodnego, który tego potrzebuje, tak jak wtedy, gdy obniżamy obiekt do oop
GET / zwierzęta /: identyfikator zwierzęcia / synowie GET / kury /: identyfikator zwierzęcia / jaja POST / kury /: identyfikator zwierzęcia / jaja
Wadą jest to, że ktoś mógłby przekazać identyfikator psa w celu odniesienia się do instancji kolekcji kur, ale pies nie jest kurą, więc nie byłoby niepoprawne, gdyby odpowiedź wynosiła 404 lub 400 z komunikatem powodu
Czy się mylę?
źródło
Tak, mylisz się. Relacje można również modelować zgodnie ze specyfikacjami OpenAPI, np. W ten polimorficzny sposób.
...
źródło
GET chicken/eggs
powinno również działać przy użyciu wspólnych generatorów kodu OpenAPI dla kontrolerów, ale jeszcze tego nie sprawdzałem. Może ktoś może spróbować?