Model dojrzałości REST API Leonarda Richardsona
Styczeń 12, 2024 | #rest-api
001000
010001
100001
W tym artykule:
- REST vs RESTFul
- Zasady REST Roya Fieldinga
- Model dojrzałości REST API Leonarda Richardsona
- Zasoby są kluczowym elementem architektury REST
- Czasowniki HTTP a operacje REST API
- Odpowiedzi z RESTful API
- Samoopisujące się REST API
Więcej w temacie REST API
REST vs RESTFul
Terminy "REST" i "RESTful" odnoszą się do architektury oprogramowania i sposobu projektowania usług internetowych. O ile REST to zbiór wytycznych i zasad projektowych dla usług sieciowych, o tyle RESTful to określenie używane do opisania usług lub API, które przestrzegają tych zasad. Czyli, gdy mówimy, że API jest "RESTful", oznacza to, że zostało zaprojektowane zgodnie z zasadami REST.
Zasady REST Roya Fieldinga
Specyfikacje opisujące zasady REST (Representational State Transfer) nie są zawarte w jednym formalnym dokumencie, tak jak ma to miejsce w przypadku wielu innych standardów technologicznych. Zasady REST zostały pierwotnie sformułowane przez Roya Fieldinga w jego rozprawie doktorskiej z 2000 roku pt. "Architectural Styles and the Design of Network-based Software Architectures".
Oto kluczowe aspekty opisane przez Fieldinga, które definiują architekturę REST:
Bezstanowość (Statelessness): Każde zapytanie od klienta do serwera musi zawierać wszystkie informacje potrzebne do zrozumienia i przetworzenia zapytania. Serwer nie przechowuje informacji o stanie klienta między zapytaniami.
Buforowalność (Cacheable): Odpowiedzi muszą być jasno zdefiniowane jako możliwe do buforowania lub nie, co pozwala klientom na buforowanie odpowiedzi w celu poprawy wydajności.
Klient-Serwer (Client-Server): Funkcje systemu są podzielone między serwery, które dostarczają zasoby lub usługi, i klientów, którzy korzystają z tych usług, co ułatwia skalowalność i portowalność.
Jednolity interfejs (Uniform Interface): Uproszczenie i dekupling architektury, co pozwala na niezależny rozwój różnych części aplikacji. To obejmuje użycie standardowych metod HTTP i komunikację poprzez zasoby, które są zazwyczaj reprezentowane jako URL-e.
System warstwowy (Layered System): Klient nie musi wiedzieć, czy komunikuje się bezpośrednio z serwerem końcowym, czy z pośrednikiem, co umożliwia skalowanie i zwiększa bezpieczeństwo.
Kod na żądanie (Code on Demand): Serwery mogą (jest to warunek opcjonalny) tymczasowo rozszerzyć lub dostosować funkcjonalność klienta przez przesyłanie kodu wykonywalnego.
Ponieważ specyfikacja REST jest bardziej zbiorem wytycznych niż ścisłym standardem, implementacje RESTful mogą różnić się między sobą, ale wszystkie powinny przestrzegać powyższych ogólnych zasad.
Model dojrzałości Richardsona
W 2008 roku podczas prezentacji współprowadzonej z Martinem Fowlerem, znanym autorem i ekspertem w dziedzinie architektury oprogramowania Leonard Richardson zaprezentował opracowany przez siebie model klasyfikacji REST API na podstawie ich zgodności z zasadami architektury REST zdefiniowanymi przez Roya Fieldinga.
Richardson Maturity Model (RMM) składa się z czterech poziomów (0-3), gdzie każdy kolejny poziom reprezentuje wyższy stopień dojrzałości i zgodności z ideą REST.
Poziom 0 - Bagno POX (The Swamp of POX): Na tym poziomie, API nie stosuje żadnych zasad REST. Zwykle jest to pojedynczy URI, który reaguje na różne komendy przekazywane w ciele żądania. POX oznacza "Plain Old XML" i odnosi się do interfejsów sieciowych opierających się na prostych XML-owych protokołach.
Poziom 1 - Zasoby (Resources): Na tym poziomie, API jest zorganizowane wokół zasobów, ale nie wykorzystuje jednolitego interfejsu (metod HTTP) do manipulacji tymi zasobami.
Poziom 2 - Czasowniki HTTP (HTTP Verbs): API na tym poziomie korzysta z odpowiednich metod HTTP (GET, POST, PUT, DELETE itd.) do operacji na zasobach, oraz z kodów stanu HTTP do reprezentowania wyników operacji. To zbliża API do pełnej zgodności z REST.
Poziom 3 - Hipermedia jako silnik stanu aplikacji (HATEOAS): Tutaj hipermedia wchodzą w grę. Na tym poziomie, API w pełni stosuje zasadę HATEOAS. Odpowiedzi serwera zawierają hiperłącza, które informują klienta o możliwych kolejnych krokach, które może podjąć. To znaczy, że stan aplikacji jest napędzany przez hipermedia, a klienty API są w pełni naprowadzane przez odpowiedzi serwera.
Zasoby są kluczowym elementem architektury REST
Zasoby odnoszą się do różnych obiektów, danych lub usług, które są dostępne przez API dla klientów.
Każdy zasób REST API jest unikalnie identyfikowany za pomocą URI (Uniform Resource Identifier).
W RESTful API URI przyjmuje formę URL-a identyfikując zasób i dając do niego dostęp. Składa się on ze standardowych elementów takich jak protokół, host, port, ścieżka i parametry.
Ścieżka odnosi się do konkretnego zasobu lub grupy zasobów w API. Jest to hierarchiczna struktura, która może zawierać kilka segmentów.
Zasoby definiujemy używając rzeczowników w liczbie mnogiej. np /users
, natomiast do konkretnego użytkownika odwołujemy się używając jego jednoznacznego identyfikatora np. /users/123
albo /articles/rest-api-best-practices
Jeśli jakiś zasób występuje tylko w liczbie pojedynczej dopuszczalne jest użycie rzeczownika w liczbie pojedynczej np. /me
- odnoszące się do obiektu aktualnie zalogowanego użytkownika, albo /users/123/cart
, ale są to raczej wyjątki.
Możemy zagnieżdżać zasoby np /users/123/orders/456
ale dobrze jest nie przekraczać dwóch poziomów zagnieżdżenia i mieć możliwie jak najbardziej płaską strukturę zasobów.
Zamiast brnąć w zagnieżdżenia
/users/123/orders
i /users/123/orders/456/items
Lepie użyć filtrowania
można zrobić /orders?user_id=123
i /order-items/order_id=456
Parametry zapytania (Query Parameters) mogą być dodane do URI, aby dostosować żądanie
Parametry zapytania są dodawane po ścieżce i rozpoczynają się od znaku zapytania ?
, a poszczególne parametry są od siebie oddzielone znakiem &
. Dzięki temu na jednym zasobie możemy wykonać równocześnie kilka operacji. Mogą to być:
filtrowanie np.
/products/price__lt=100
- zwróci produkty tańsze niż 100 PLN. Musimy zadbać aby nasz backend rozumiał co oznaczają sufiksy__lt
,__gte
,__eq
itd. To akurat przykład konwencji zaczerpnięty ze świata Django.wyszukiwanie np.
/books?q=Harry
- będzie szukać książek zawierających słowo "Harry" np. w tytule, opisie, w nazwie autora. W tym przypadku parametrq
"rezerwujemy" dla funkcjonalności wyszukiwania i określamy jakie pola dla danego zasobu mają być przeszukiwane podczas tej operacji.sortowanie
/authors?sort=-last_name,first_name
- tym razem parametrsort
jest dedykowany funkcjonalności sortowania. Podajemy w nim nazwy pól rozdzielonych przecinkiem po jakich ma nastąpić sortowanie. Kolejność pól ma znaczenie. Znak minus-
przed nazwą pola oznacza, że sortowanie po tym polu ma się odbywać w odwrotnej kolejności (descending).paginacja
/products?page=7&per_page=50
ograniczanie lub rozszerzanie wyniku odpowiedzi o dodatkowe dane -
/alubms/23?embeded=artist.name,title
- w podanym przykładzie chcemy otrzymać dane albumu ograniczone do jego tytułu i poszerzone o nazwę powiązanego z nim artysty.
Powyższe operacje nie wyczerpują katalogu możliwości.
Czasowniki HTTP a operacje REST API
O ile zasoby opisywane są rzeczownikami o tyle metody HTTP - przynajmniej te podstawowe jak GET
, POST
, PUT
, PATCH
, and DELETE
są w formie czasowników.
Standardowe metody HTTP
Metody HTTP są podstawą dla interakcji klient-serwer w REST API i pozwalają na wyraźne i jednoznaczne określenie intencji żądania.
Każda z tych metod ma określone znaczenie i jest używana w specyficznych scenariuszach. Oto najczęściej używane metody HTTP w REST API:
GET: Używana do pobierania danych z serwera. RFC 2616 - HTTP 1.1 section 9.1.1 opisuje metodę GET jako bezpieczną, co oznacza, że:
- nie powoduje zmiany stanu zasobu
- jest idempotentna - wielokrotne wywołanie tej samej metody GET daje ten sam wynik.
POST: Używana do tworzenia nowego zasobu na serwerze. Nie jest idempotentna, co oznacza, że wielokrotne wysłanie tego samego żądania POST może skutkować utworzeniem wielu zasobów.
PUT: Używana do aktualizowania istniejącego zasobu lub tworzenia nowego, jeśli nie istnieje. PUT jest idempotentna, więc wielokrotne wykonanie PUT z tymi samymi danymi na tym samym zasobie skutkuje tym samym stanem zasobu.
DELETE: Używana do usunięcia zasobu z serwera. Tak jak PUT, DELETE jest również idempotentna, co oznacza, że wielokrotne wywołanie DELETE na tym samym zasobie daje ten sam wynik (zasób pozostaje usunięty).
PATCH: Używana do częściowej modyfikacji istniejącego zasobu. PATCH jest mniej restrykcyjna niż PUT, ponieważ pozwala na aktualizację części zasobu, bez konieczności dostarczania pełnej reprezentacji.
Wyżej wymienione metody umożliwiają realizację czterech podstawowych operacji określonych akronimem CRUD.
C - Create (Tworzenie): tworzenie nowych danych lub zasobów - POST.
R - Read (Odczyt): odczytywanie lub pobierania istniejących danych - GET
U - Update (Aktualizacja): aktualizacja istniejących danych lub zasobów - PUT lub PATCH
D - Delete (Usuwanie): usuwanie danych lub zasobów - DELETE.
Dodatkowe metody HTTP
Mamy jeszcze inne metody HTTP takie Jak OPTIONS
, HEAD
, TRACE
. Wiele popularnych frameworków do tworzenia REST API, takich jak Django Rest Framework (DRF) i FastAPI, automatycznie implementuje obsługę dla nagłówków OPTIONS
i HEAD
więc zwykle nie ma potrzeby modyfikacji ich obsługi. Zawsze jednak warto wiedzieć jak działają. Co do metody TRACE
to jest ona zwykle pomijana z powodów, które opisuję niżej.
OPTIONS
Metod OPTIONS
jest używana w REST API oraz w ogólnym kontekście protokołu HTTP do określenia opcji komunikacji z zasobem lub serwerem. Gdy klient wysyła żądanie OPTIONS
, serwer odpowiada, wskazując, które metody HTTP są dozwolone dla danego zasobu. Odpowiedź może również zawierać inne informacje, takie jak nagłówki, które mogą być używane w żądaniach.
W przypadku żądań międzydomenowych, metoda OPTIONS
jest często wykorzystywana jako "pre-flight request", gdzie przeglądarka automatycznie wysyła żądanie OPTIONS
przed właściwym żądaniem (np. POST, PUT), aby sprawdzić, czy serwer zezwala na takie żądanie z poziomu innej domeny.
# żądanie
OPTIONS /api/resource
# Odpowiedź
HTTP/1.1 204 No Content
Allow: GET, POST, HEAD, OPTIONS
HEAD
Metoda HTTP HEAD
jest podobna do metody GET
, ale z tą istotną różnicą, że serwer w odpowiedzi na żądanie HEAD
zwraca tylko nagłówki HTTP, bez ciała odpowiedzi. Metoda HEAD
pozwala klientowi sprawdzić dostępność zasobu i/lub metadane z nim związane, takie jak typ zawartości (Content-Type), długość zawartości (Content-Length), ostatnia modyfikacja (Last-Modified) i inne nagłówki, bez konieczności pobierania całości.
# żądanie
HEAD /api/resource
# odpowiedź
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 12345
Last-Modified: Wed, 21 Oct 2020 07:28:00 GMT
TRACE
Metoda HTTP TRACE
jest stosunkowo rzadko używaną metodą, która służy do diagnostyki i debugowania w sieciach komputerowych. Głównym zadaniem TRACE
jest odbicie żądania tak, aby klient mógł zobaczyć, co zostało otrzymane przez serwer, co może być przydatne w identyfikacji zmian lub dodatków dokonanych przez pośredników w ścieżce żądania.
Metoda TRACE
jest często wyłączana na serwerach ze względów bezpieczeństwa. Jednym z zagrożeń związanym z TRACE
jest potencjalne wykorzystanie w atakach typu Cross-Site Scripting (XSS), gdy serwer odbija treść żądania, która może zawierać szkodliwy skrypt.
# żądanie
TRACE /api/example
Header1: Value1
Header2: Value2
# odpowiedź
HTTP/1.1 200 OK
Content-Type: message/http
TRACE /api/example HTTP/1.1
Host: example.com
Header1: Value1
Header2: Value2
Punkt końcowy
Metoda HTTP + URI tworzy punkt końcowy (endpoint). Innymi słowy zasób i operacja, którą chcemy na tym zasobie wykonać tworzą dopiero konkretny punkt końcowy.
Odpowiedzi z RESTful API
Odpowiedzi z RESTful API powinny być jasno zdefiniowane, zrozumiałe i spójne, aby ułatwić korzystanie z API.
Kody statusu HTTP
Odpowiedzi powinny zawierać odpowiednie kody statusu HTTP, aby wskazać, czy żądanie zostało przetworzone pomyślnie czy nie. Tradycyjnie pewnym operacjom towarzyszą standardowe kody statusów.
Status 200 OK oznacza że udało się pobrać lub odczytać obiekt lub listę obiektów metodą GET
. W przypadku nie odnalezienia zasobu zwykle zwracane jest 404 Not Found, ale jeśli ma to znaczenie możemy też zwrócić 410 Gone dając do zrozumienia, że ten zasób istniał, ale został trwale usunięty (np. przeniesiony do archiwum o czym informujemy w treści komunikatu.)
Status 201 Created zwykle towarzyszy metodzie POST
informując o powodzeniu utworzenia nowego obiektu. Jeśli zostaną przesłane niepoprawne dane, co do typu, formatu itp. zwracany jest błąd 400 Bad Request. W przypadku przesłania poprawnych danych ale naruszających np. warunek integralności (unikalności) mamy możliwość zwrócenia bardziej precyzyjnego statusu 409 Conflict z reguły jednak najczęściej obsługiwane jest to zwykłą 400.
Powodzenie aktualizacji metodą PUT
lub PATCH
też zwykle kończy się kodem 200 OK, ale może równie dobrze kończyć się kodem 204 No Content, tak jak ma to miejsce w przypadku operacji usuwania - DELETE
. To jaki kod należy zwrócić, zależy od tego czy po wykonaniu operacji zwracamy zaktualizowany obiekt lub link do niego, czy też nie zwracamy żadnych danych.
Dobrą praktyką jest zwracanie nowo utworzonej instancji w odpowiedzi metody POST
, tak jak i zaktualizowanych danych odpowiednio dla metod PUT
czy PATCH
. Zwiększa to co prawda wielkość odpowiedzi ale najczęściej zapobiega konieczności pobrania nowego, czy zaktualizowanego zasobu osobnym żądaniem.
Format danych
Format danych dotyczy zarówno żądania, ale przede wszystkim odpowiedzi. Choć obecnie niepodzielnie "rządzi" JSON, jest o wiele więcej formatów, które są używane.
Oto najpopularniejsze formaty danych używane w REST API:
JSON (JavaScript Object Notation): Jest to najczęściej używany format w REST API ze względu na jego lekkość, czytelność i łatwość obsługi w większości języków programowania. JSON jest idealny dla większości zastosowań internetowych.
XML (eXtensible Markup Language): XML jest nadal używany, szczególnie w starszych systemach i w branżach, które przyjęły go jako standard (np. w finansach i telekomunikacji). XML jest dobrze znany ze swojej rygorystycznej struktury i zdolności do reprezentowania złożonych struktur danych.
HTML (Hypertext Markup Language): Chociaż rzadko używany jako główny format odpowiedzi w typowych REST API, HTML może być przydatny, gdy API jest przeznaczone do bezpośredniego wyświetlania danych w przeglądarkach internetowych.
YAML (YAML Ain't Markup Language): YAML jest czasem używany w API ze względu na swoją czytelność i prostotę, zwłaszcza w konfiguracjach lub w API skierowanych do deweloperów.
PlainText: Prosty tekst może być używany dla bardzo prostych odpowiedzi, na przykład dla API zwracających tylko jedną wartość lub krótki komunikat.
CSV (Comma-Separated Values): Format CSV jest przydatny w przypadku API, które dostarczają danych w formie tabelarycznej, co jest użyteczne np. dla aplikacji do analizy danych czy arkuszy kalkulacyjnych.
Binary data: Dla API zwracających pliki lub dane binarne (np. obrazy, dokumenty PDF, pliki audio), odpowiedzi mogą być w formacie binarnym.
Wybór formatu danych zależy od specyfiki API i potrzeb jego użytkowników. Wiele API oferuje możliwość wyboru formatu danych przez klienta, na przykład poprzez użycie nagłówków HTTP (np. Accept: application/json
) lub parametru w adresie URL /article?format=html
To pozwala na większą elastyczność i może służyć różnym klientom API.
Ciekawą opcją jest też implementacja różnych kształtów odpowiedzi w ramach tego samego formatu np.
/categories?format=json
- domyślnie zwraca wynik w postaci listy obiektów/categories?format=json-tree
- alternatywnie zwraca wynik w formie drzewa
{
"categories": [
{
"id": 1,
"name": "Electronics",
"subcategories": [
{
"id": 101,
"name": "Computers"
},
{
"id": 102,
"name": "Mobile Phones"
}
]
},
{
"id": 2,
"name": "Home & Garden",
"subcategories": [
{
"id": 201,
"name": "Furniture"
},
{
"id": 202,
"name": "Gardening"
}
]
},
{
"id": 3,
"name": "Automotive",
"subcategories": []
}
]
}
Taka zagnieżdżona struktura drzewiasta jest przydatna, na przykład, w sklepach internetowych lub innych systemach, które wymagają organizacji danych w hierarchicznej formie. Klienci API mogą używać tych danych do wyświetlania struktury kategorii w interfejsie użytkownika lub do innych celów nawigacyjnych.
Samoopisujące się REST API
HATEOAS (Hypermedia as the Engine of Application State) to element architektury aplikacji REST, które odróżnia ją od innych architektur aplikacji sieciowych.
Zasada HATEOAS podkreśla, że klient aplikacji powinien wchodzić w interakcję z aplikacją wyłącznie przez hipermedia, dostarczane dynamicznie przez serwer. Oznacza to, że klient API odkrywa dostępne akcje i zasoby za pośrednictwem hiperłączy zawartych w odpowiedziach od serwera, a nie przez wcześniejsze zakodowanie URI czy akcji. HATEOAS jest sposobem wykorzystania hipermediów do zarządzania stanem aplikacji. Podejście to ma na celu stworzenie bardziej odkrywalnego i samoopisującego się API.
Po wejściu na główny adres API githuba widzimy listę dostępnych zasobów, po których możemy poruszać się jak po stronie internetowej.
# Żądanie klienta
GET https://api.github.com
# Odpowiedź serwera
{
"current_user_url": "https://api.github.com/user",
"current_user_authorizations_html_url": "https://github.com/settings/connections/applications{/client_id}",
"authorizations_url": "https://api.github.com/authorizations",
...
}
Podążając za zamieszonymi linkami uzyskujemy dostęp do kolejnych zasobów albo otrzymujemy dodatkowe informacje np link do dokumentacji.
# Żądanie klienta
GET https://api.github.com/user
# Odpowiedź serwera
{
"message": "Requires authentication",
"documentation_url": "https://docs.github.com/rest/users/users#get-the-authenticated-user"
}
Łącza hipermedialne mogą być realizowane w różnych formatach. Najbardziej popularne to JSON REST API i HAL, ale mogą Cię również zainteresować Siren, czy JSON-LD
Łącza hipermedialne w formacie JSON REST API
Referencje JSON REST API są opisane w specyfikacji RFC 5988 wedle której łącze powinno składać się z
Docelowy identyfikator IRI : każde łącze powinno zawierać docelowy międzynarodowy identyfikator zasobów (IRI). Jest to reprezentowane przez
href
atrybut.Typ relacji łącza : typ relacji łącza opisuje, w jaki sposób bieżący kontekst (źródło) jest powiązany z zasobem docelowym. Jest to reprezentowane przez
rel
atrybut.Atrybuty docelowego IRI : Atrybuty łącza obejmują
hreflang
,media
,title
itype
oraz wszelkie parametry łącza rozszerzenia.
W praktyce może to wyglądać następująco
[{
"id": 42,
"title": "Harry Potter i kamień filozoficzny",
"author": "J.K. Rowling",
"isbn": "978-83-800-8211-3",
"links": [
{
"rel": "self",
"href": "https://example.com/books/42"
},
{
"rel": "author",
"href": "https://example.com/authors/123"
},
{
"rel": "reviews",
"href": "https://example.com/books/42/reviews",
}
]
}]
Wartym uwagi w powyższym przykładzie jest odsyłacz do recenzji /reviews
, które możemy odczytać metodą GET
uzyskując listę recenzji, ale też dodać nową recenzję używając metody POST
. Informację o dozwolonych metodach HTTP dla zasobu uzyskamy dzięki nagłówkowi Allow
w odpowiedzi HTTP na żądanie wykonane metodą OPTIONS
.
HTTP/1.1 200 OK
Allow: GET, POST
Content-Length: 0
Informacje w nagłówkach też są elementem samoopisującego się API i jak najbardziej wpisują się w ideę HATEOAS.
Hipermedia API w języku HAL
Hypertext Application Language (HAL) to konwencja definiowania relacji pomiędzy zasobami API REST w formacie JSON lub XML. Pomimo tego, że standard ten został pierwotnie zaproponowany w 2012 roku nie doczekał się jeszcze wersji końcowej i wciąż ma status projektu.
Hipermedia opisane tym językiem powinny zawierać
Docelowy URI : Wskazuje docelowy URI zasobu. Jest to reprezentowane przez
href
atrybut.Relacja łącza : typ relacji łącza opisuje, w jaki sposób bieżący kontekst jest powiązany z zasobem docelowym. Jest to reprezentowane przez
rel
atrybut.Typ : wskazuje oczekiwany typ nośnika zasobu. Jest to reprezentowane przez
type
atrybut.
{
"_links": {
"self": { "href": "https://example.com/books/42" },
"author": { "href": "https://example.com/authors/123" }
},
"_embedded": {
"author": {
"_links": {
"self": { "href": "https://example.com/authors/123" }
},
"id": 123,
"name": "J.K. Rowling"
}
},
"id": 42,
"title": "Harry Potter i kamień filozoficzny",
"author": "J.K. Rowling",
"isbn": "978-83-800-8211-3",
}
W tym przykładzie, _links
zawiera hiperłącza do zasobu książki i jej autora, a _embedded
zawiera zagnieżdżony zasób autora z własnymi hiperłączami. Użycie HAL w JSON REST API ułatwia implementację HATEOAS, dostarczając standardowy sposób na reprezentację hiperłączy i zagnieżdżonych zasobów.
Zalety i wady HATEOAS
Odkrywalność
Klienci nie muszą wiedzieć z góry, jak zbudowane są URL-e do kolejnych stron, ponieważ otrzymują je bezpośrednio w odpowiedziach.
Przykłady
Odpowiedź na żądanie o listę artykułów
/articles?category_id=3
dostarcza automatycznie hiperłącza do kategorii, po której nastąpiło filtrowanie/categories/3
, szczegółów każdego z artykułów, linków do kolejnej i/lub poprzedniej strony paginacjiOdpowiedź na żądanie o szczegóły artykułu
/artilcles/9
dostarcza hiperłączy do zasobów powiązanych np. informacji o autorach/artilcels/9/authors
, zdjęć/articles/9/photos
, komentarzy dotyczących artykułu/articles/9/comments
itd.
Dostarczanie gotowego odnośnika pozwala po stronie klienta API na opracowanie generycznych rozwiązań opartych o wartość atrybutu rel
opisującego rodzaj relacji a także atrybutów towarzyszących jak type
, media
, title
itp.
W praktyce wiąże się to z dodatkową warstwą złożoności po stronie klienta co może zwiększyć czas i koszt rozwoju. Wymagania dotyczące designu czy wygody użytkowania UX (user experience) często utrudniają lub wręcz uniemożliwiają implementację zunifikowanych flow zmuszając deweloperów do "hardcodowania" pewnych fragmentów kodu aplikacji i z tych powodów użyteczność mechanizmów HATEOAS w tym zakresie może być ograniczona.
Elastyczność
Serwer może zmieniać strukturę URL-i bez wpływu na klientów, ponieważ linki są dynamicznie generowane i dostarczane w odpowiedziach. Backend może decydować, które akcje są dostępne, dzięki czemu interfejs użytkownika nie musi znać logiki biznesowej.
Przykłady
- Hiperłącza do zasobów powiązanych są generowane tylko jak użytkownik jest zalogowany i ma do nich dostęp np. link do kodów promocyjnych użytkownika
/users/4/promocodes
. Frontend nie musi odpytywać o uprawnienia ani znać zasad przyznawania kodów promocyjnych na podstawie aktywności użytkownika. - Jeśli do artykułu został dołączony film pojawia się hiperłącze odsyłające do tego zasobu
/articles/8/movies
Jako programista klienta musisz być przygotowany na obsługę zasobów, które mogą się "pojawić" dlatego zamiast znikających odsyłaczy być może wolisz po prostu odpytać punkt końcowy, a o dostępności zasobu wywnioskujesz z kod statusu np. 404 Not Found
, albo 403 Forbidden
. Z drugiej strony o tym, że coś jest niedostępne dowiesz się dopiero kiedy zażądasz odpowiedzi, podczas gdy brak odsyłacza da Ci tę informację natychmiast. Możesz ją łatwo skorelować z mechanizmem ukrywania/deaktywacji tych części interfejsu, które są związane z nieosiągalnym zasobem.
Samoopisowość
Odpowiedzi zawierające linki HATEOAS są bardziej samoopisowe. Ułatwia to zrozumienie i wykorzystanie API przez nowych użytkowników i deweloperów, ale wciąż nie zastąpi to poprawnie przygotowanej dokumentacji wzbogaconej o sandbox. Ponadto należy wspomnieć, o rozmiarach odpowiedzi, które puchną pod wpływem dodatkowych danych. Choć hipermedia niewątpliwie ubogacają ładunek informacyjny niesiony w odpowiedzi niekiedy priorytet stawia się na wydajność i przepustowość. Samoopisowość API ma znaczenie na etapie dewelopmentu, natomiast w chwili działania może stanowić już tylko zbędne obciążenie dlatego dobrą praktyką jest taka implementacja metadanych w odpowiedzi z API REST aby dało się je deaktywować w środowisku produkcyjnym gdzie każdy zbędny bajt przesłany do klienta robi różnicę, albo jak w przypadku publicznych API dać klientowi możliwość decydowania za pomocą parametrów lub nagłówka czy chce odpowiedziach z API z HATEOAS.
Accept: application/hal+json