Dzisiaj ostatnia warstwa modelu, która zdecydowanie często jest pomijana w implementacjach REST. Moim zdaniem, w przypadku publicznych API jest bardzo ważna, szczególnie w środowisku mikro-serwisów, gdzie nawigacja jest utrudniona ze względu na liczbę usług.
HATEOAS to skrót od Hypertext As The Engine Of Application State. Mechanizm dostarcza możliwość nawigacji przez zasoby bez wiedzy o konkretnych adresach URL. Załóżmy, że mamy bazę klientów w systemie i możemy w niej:
- Wylistować listę klientów.
- Zwrócić dane konkretnego klienta.
- Aktualizować dane.
- Usuwać klienta z bazy.
- Zwracać poszczególne dane takie jak dane kontaktowe, adres itp.
Projektując serwis zgodnie z poprzednimi zasadami, otrzymalibyśmy następujące linki:
- GET /customers
- GET /customers/{id}
- PUT /customers i w ciele dane klienta
- DELETE /customers/{id}
- GET customers/{id}/email, customers/{id}/address itd.
Bez warstwy trzeciej, klient musi znać te linki. Innymi słowy, klient musi znać implementację wewnętrzną serwera i w przypadku jakiejkolwiek zmiany, wszyscy klienci muszą zaktualizować kod. Klient tak naprawdę powinien tylko wiedzieć co chce zrobić, a nie jak to ma zrobić.
W przypadku hateoas, konsument musi znać dane tylko korzenia. Załóżmy, że wykonujemy zapytanie HTTP GET /customers, aby wylistować wszystkie osoby w bazie. Jako odpowiedz przyjdzie coś w rodzaju:
HTTP/1.1 200 OK <Customers> <Customer FirstName="Piotr" Last="Zielinski" Id="1"> <link rel = "details" uri = "/customers/1"/> <link rel = "address" uri = "/customers/1/address"/> </Customer> <Customer FirstName="afaf" Last="sfsagsa" Id="2"> <link rel = "details" uri = "/customers/2"/> <link rel = "address" uri = "/customers/1/address"/> </Customer> </Customers>
Analogicznie, dodając nową osobę do bazy czyli HTTP POST /customers/, dostaniemy w odpowiedzi również mapę linków:
HTTP/1.1 201 Created <Customer FirstName="Piotr" Last="Zielinski" Id="1"> <link rel = "details" uri = "/customers/1"/> <link rel = "address" uri = "/customers/address"/> </Customer>
Jak widzimy, to serwer decyduje, co klient może zrobić. Odpowiedź określa również aktualny stan aplikacji, czyli co możemy ze zwróconymi zasobami, w danym momencie zrobić. API zaprojektowane w ten sposób ma zatem wbudowany mechanizm discovery – przekazujemy konsumentowi tylko korzeń, a on sam już jest w stanie przeglądać i wykonywać dowolne operacje na zasobach.