Jakie są najlepsze praktyki dla zasobów zagnieżdżonych REST

O ile mogę powiedzieć, każdy pojedynczy zasób powinien mieć tylko jedną kanoniczną ścieżkę . Więc w poniższym przykładzie jakie byłyby dobre wzorce URL?

Weźmy na przykład reprezentację firm w stanie spoczynku. W tym hipotetycznym przykładzie każda firma posiada 0 lub więcej działów, a każdy dział posiada 0 lub więcej pracowników.

Dział nie może istnieć bez powiązanej firmy.

Pracownik nie może exist bez powiązanego działu.

Teraz znajdowałbym naturalną reprezentację wzorców zasobów.

  • /companies zbiór firm - przyjmuje put dla nowej firmy. Zdobądź całą kolekcję.
  • /companies/{companyId} indywidualna firma. Akceptuje GET, PUT I DELETE
  • /companies/{companyId}/departments akceptuje POST dla nowego artykułu. (Tworzy dział w ramach Towarzystwo.)
  • /companies/{companyId}/departments/{departmentId}/
  • /companies/{companyId}/departments/{departmentId}/employees
  • /companies/{companyId}/departments/{departmentId}/employees/{empId}

Biorąc pod uwagę ograniczenia, w każdej z sekcji czuję, że ma to sens, jeśli jest trochę głęboko zagnieżdżone.

Jednak moja trudność przychodzi, jeśli chcę wymienić (GET) wszystkich pracowników we wszystkich firmach.

W 2007 roku, po raz pierwszy w historii firmy, w 2008 roku, firma została założona w 2009 roku.]}

Czy to znaczy, że powinienem mieć /employees/{empId} również dlatego, że jeśli tak, to są dwa URI do uzyskać ten sam zasób?

A może cały schemat powinien zostać spłaszczony, ale oznaczałoby to, że pracownicy są zagnieżdżonym obiektem najwyższego poziomu.

Na poziomie podstawowym /employees/?company={companyId}&department={deptId} zwraca dokładnie ten sam widok pracowników, co najgłębiej zagnieżdżony wzór.

Jaka jest najlepsza praktyka dla wzorców URL, gdzie zasoby są własnością przez inne zasoby, ale powinny być dostępne osobno?


UPDATE: zobacz moją odpowiedź poniżej, aby zobaczyć, co mam załatwione.

 198
Author: Damaged Organic, 2014-01-06

6 answers

To, co zrobiłeś, jest poprawne. Ogólnie rzecz biorąc, może być wiele Uri do tego samego zasobu - nie ma zasad, które mówią, że nie powinieneś tego robić.

I ogólnie rzecz biorąc, możesz potrzebować dostępu do elementów bezpośrednio lub jako podzbiór czegoś innego-więc twoja struktura ma dla mnie sens.

Tylko dlatego, że pracownicy są dostępni w dziale:

company/{companyid}/department/{departmentid}/employees

Nie znaczy, że nie mogą być dostępne również pod firmą:

company/{companyid}/employees

Które zwracałyby pracowników za ta firma. To zależy od tego, czego potrzebuje twój konsumujący klient - do tego powinieneś projektować.

Ale mam nadzieję, że wszystkie programy obsługi adresów URL używają tego samego kodu pomocniczego, aby zaspokoić żądania, abyś nie powielał kodu.

 100
Author: jeremyh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-08-18 14:21:58

Wypróbowałem obie strategie projektowania-zagnieżdżone i nie zagnieżdżone punkty końcowe. Znalazłem to:

  1. Jeśli zagnieżdżony zasób ma klucz podstawowy, a ty nie masz jego klucza nadrzędnego, zagnieżdżona struktura wymaga, aby go uzyskać, nawet jeśli system w rzeczywistości tego nie wymaga.

  2. Zagnieżdżone punkty końcowe zazwyczaj wymagają zbędnych punktów końcowych. Innymi słowy, często będziesz potrzebować dodatkowego punktu końcowego / pracowników, aby uzyskać listę pracowników w różnych departamentach. Jeśli masz / pracowników, co dokładnie kupuje/firmy/działy / pracownicy?

  3. Punkty końcowe zagnieżdżania nie ewoluują tak ładnie. Na przykład może nie trzeba szukać pracowników teraz, ale może później, a jeśli masz zagnieżdżoną strukturę, nie masz innego wyboru, jak dodać inny punkt końcowy. W przypadku projektu bez zagnieżdżenia wystarczy dodać więcej parametrów, co jest prostsze.

  4. Czasami zasób może mieć wiele typów rodziców. W wyniku czego powstaje wiele wszystkie zwracają ten sam zasób.

  5. Redundantne punkty końcowe utrudnia pisanie dokumentów, a także utrudnia naukę interfejsu api.

Krótko mówiąc, projekt bez zagnieżdżenia wydaje się pozwalać na bardziej elastyczny i prostszy schemat punktu końcowego.

 98
Author: Patc,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-04-04 19:01:54

Przeniosłem to, co zrobiłem z Pytania do odpowiedzi, gdzie więcej osób może to zobaczyć.

To, co zrobiłem, to mieć punkty końcowe tworzenia w zagnieżdżonym punkcie końcowym, kanonicznym punktem końcowym modyfikacji lub zapytania elementu jest , a nie w zagnieżdżonym zasobie.

Tak więc w tym przykładzie (wystarczy wymienić punkty końcowe, które zmieniają zasób)

  • POST /companies/ tworzy nową firmę zwraca link do utworzonego Towarzystwo.
  • POST /companies/{companyId}/departments gdy dział zostanie utworzony, nowy dział zwróci link do /departments/{departmentId}
  • PUT /departments/{departmentId} modyfikuje dział
  • POST /departments/{deparmentId}/employees utworzenie nowego pracownika zwraca link do /employees/{employeeId}

Więc istnieją zasoby na poziomie głównym dla każdej z kolekcji. Jednak create znajduje się w obiekcie posiadającym.

 52
Author: Wes,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-29 12:11:44

Nie zgadzam się z tego rodzaju ścieżką

GET /companies/{companyId}/departments

Jeśli chcesz mieć działy, myślę, że lepiej jest użyć zasobu / działów

GET /departments?companyId=123

Przypuszczam, że masz companies tabelę i departments tabelę, a następnie klasy do mapowania ich w języku programowania, którego używasz. Zakładam również, że działy mogą być dołączone do innych podmiotów niż firmy, więc zasób a / departamentów jest prosty, wygodnie jest mieć zasoby mapowane do tabel, a także nie potrzebujesz tak wielu punktów końcowych ponieważ możesz ponownie użyć

GET /departments?companyId=123

Dla każdego rodzaju Wyszukiwania, na przykład

GET /departments?name=xxx
GET /departments?companyId=123&name=xxx
etc.

Jeśli chcesz utworzyć dział,

POST /departments

Należy użyć zasobu, a organ żądający powinien zawierać identyfikator firmy (jeśli dział może być połączony tylko z jedną firmą).

 6
Author: Maxime Laval,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-02-13 18:54:23

Wygląd adresów URL nie ma nic wspólnego z resztą. Wszystko idzie. W rzeczywistości jest to "szczegół realizacji". Tak jak nazywacie swoje zmienne. Wszystko, co musi być wyjątkowe i trwałe.

Nie trać na to zbyt wiele czasu, po prostu dokonaj wyboru i trzymaj się go / bądź konsekwentny. Na przykład, jeśli idziesz z hierarchiami, to robisz to dla wszystkich swoich zasobów. Jeśli wybierzesz parametry zapytania...itp. Podobnie jak konwencje nazewnictwa w Twoim kodzie. Dlaczego tak ? Z tego co wiem a "RESTful" API ma być przeglądane (wiesz..."Hypermedia jako silnik stanu aplikacji"), dlatego klient API nie dba o to, jakie są Twoje adresy URL, o ile są ważne (nie ma SEO, nie ma człowieka, który musi czytać te "przyjazne adresy URL", z wyjątkiem może być do debugowania...)

Jak ładny / zrozumiały jest adres URL w REST API jest interesujący tylko dla Ciebie jako programisty API, a nie klienta API, tak jak nazwa zmiennej w Twoim kodzie.

Najbardziej ważne jest to, że klient API wie, jak interpretować typ multimediów. Na przykład wie, że:

  • Twój typ mediów ma właściwość linki, która wyświetla dostępne / powiązane linki.
  • każdy link jest identyfikowany przez relację (tak jak przeglądarki wiedzą, że link[rel="arkusz stylów"] oznacza jego arkusz stylów lub rel=favico jest łączem do faviconu...)
  • i wie, co oznaczają te relacje ("firmy" oznaczają listę firm, "Szukaj" oznacza szablon adresu URL do wyszukiwania na liście zasobów, "działy" oznacza działy bieżącego zasobu)

Poniżej znajduje się przykład wymiany HTTP (ciała są w yaml, ponieważ łatwiej jest pisać):

Request

GET / HTTP/1.1
Host: api.acme.io
Accept: text/yaml, text/acme-mediatype+yaml

Odpowiedź: lista linków do głównego zasobu (firmy, ludzie, cokolwiek...)

HTTP/1.1 200 OK
Date: Tue, 05 Apr 2016 15:04:00 GMT
Last-Modified: Tue, 05 Apr 2016 00:00:00 GMT
Content-Type: text/acme-mediatype+yaml

# body: this is your API's entrypoint (like a homepage)  
links:
  # could be some random path https://api.acme.local/modskmklmkdsml
  # the only thing the API client cares about is the key (or rel) "companies"
  companies: https://api.acme.local/companies
  people: https://api.acme.local/people

Zapytanie: link do firm (z wykorzystaniem organu poprzedniej odpowiedzi.linki.firmy)

GET /companies HTTP/1.1
Host: api.acme.local
Accept: text/yaml, text/acme-mediatype+yaml

Odpowiedź: lista częściowa z firm (w pozycji), zasób zawiera powiązane linki, takie jak link, aby uzyskać następne kilka firm (ciało.linki.następny) inny (template) link do wyszukiwania (body.linki.Szukaj)

HTTP/1.1 200 OK
Date: Tue, 05 Apr 2016 15:06:00 GMT
Last-Modified: Tue, 05 Apr 2016 00:00:00 GMT
Content-Type: text/acme-mediatype+yaml

# body: representation of a list of companies
links:
  # link to the next page
  next: https://api.acme.local/companies?page=2
  # templated link for search
  search: https://api.acme.local/companies?query={query} 
# you could provide available actions related to this resource
actions:
  add:
    href: https://api.acme.local/companies
    method: POST
items:
  - name: company1
    links:
      self: https://api.acme.local/companies/8er13eo
      # and here is the link to departments
      # again the client only cares about the key department
      department: https://api.acme.local/companies/8er13eo/departments
  - name: company2
    links:
      self: https://api.acme.local/companies/9r13d4l
      # or could be in some other location ! 
      department: https://api2.acme.local/departments?company=8er13eo

Więc jak widzisz, jeśli idziesz link / relacje sposób jak struktura część ścieżki adresów URL nie ma żadnej wartości dla klienta API. A jeśli przekazujesz strukturę swoich adresów URL Klientowi jako dokumentację, to nie odpoczywasz (lub przynajmniej nie Poziom 3 zgodnie z "model dojrzałości Richardsona")

 4
Author: redben,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-08-26 16:11:00

Przeczytałem wszystkie powyższe odpowiedzi, ale wydaje się, że nie mają wspólnej strategii. Znalazłem dobry artykuł o najlepszych praktykach w projektowaniu API z Microsoft Documents . Myślę, że powinieneś się odnieść.

W bardziej złożonych systemach może być kuszące dostarczanie URI, które umożliwienie klientowi poruszania się po kilku poziomach relacji, takich jak /customers/1/orders/99/products., jednak ten poziom złożoność może być trudna do utrzymania i jest nieelastyczna, jeśli relacje między zasobami zmieniają się w przyszłości. zamiast tego spróbuj zachowaj stosunkowo proste Uri. Gdy wniosek zawiera odniesienie do zasobów, powinno być możliwe użycie tego odniesienia do znalezienia przedmiotów związane z tym zasobem. Poprzedzające zapytanie można zastąpić URI /customers/1/orders, aby znaleźć wszystkie zamówienia dla Klienta 1, oraz następnie /orders/99/products, aby znaleźć produkty w tej kolejności.

.

Końcówka

Unikaj konieczności stosowania Uri zasobów bardziej złożonych niż collection/item/collection.

 4
Author: Long Nguyen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-19 02:25:36