REST API – model dojrzałości Richardsona

Jak bardzo Twoje API jest REST’owe? Czy może być lepsze? Bardziej REST’owe? REST i RESTful API to pojęcia ciągle przewijające się w kontekście aplikacji internetowych. Przypomnę, że implementacja API zgodnie z założeniami architektury REST nazywana jest RESTful API. Wyobraźmy sobie przez chwilę, że napisaliśmy prostą aplikację RESTful API. Skąd tak naprawdę mamy mieć pewność, że jest w stylu REST? Ktoś może Ci powiedzieć, że skoro wykorzystujesz protokół HTTP i używasz formatu danych JSON, to usługa internetowa musi być RESTful API. Czy to jest na pewno zgodne z prawdą? W tym artykule postaram się trochę rozjaśnić sytuację omawiając Model dojrzałości Richardsona (RMM – Richardson Maturity Model). Model dojrzałości Richardsona to sposób na ocenę interfejsu API zgodnie z ograniczeniami REST. Im bardziej Twój interfejs API jest zgodny z tymi ograniczeniami, tym wyższy jest jego wynik. Model dojrzałości Richardsona definiuje 4 poziomy (0-3), gdzie poziom 3 oznacza prawdziwie RESTful API.

Level Zero: The Swamp of POX (Poziom 0)

Zgodnie z modelem dojrzałości Richardsona, każde API należy do dowolnego z poziomów dojrzałości od poziomu 0 do poziomu 3. Im wyższy poziom został zaimplementowany, tym bardziej nasze API jest zgodne z architekturą REST. Na poziomie 0 API nie spełnia założeń dla stylu architektury REST. API należące do tego poziomu nie wykorzystuje pełnego potencjału protokołu HTTP. Protokół HTTP wykorzystywany jest jako warstwa transportowa. Używane są tylko metody GET i POST oraz zawsze zwracany jest status 200 OK. Implementacja API nie opiera się na unikalnych adresach zasobów. Do tego poziomu zaliczamy protokoły SOAP i RPC.

Przykład API na poziomie 0:

POST /api/createPerson
POST /api/findPerson
POST /api/updatePerson
POST /api/removePerson

Przykład dodawania osoby:

POST /api/createPerson HTTP/1.1
{ "name":"Patryk Sładek", "age":27 }

Przykładowe odpowiedzi:

HTTP/1.1 200 OK
{ "id":36 }

lub

HTTP/1.1 200 OK
{ "error":"Person with name Patryk Sładek already exists." }

Level One: Resources (Poziom 1)

Na pierwszym poziomie modelu dojrzałości Richardsona, usługi wykorzystują wiele identyfikatorów URI i nadal tylko metody HTTP GET i POST.  Poziom pierwszy definiuje pojęcia zasobu. Zasób to obiekt (dowolna informacja), do której uzyskujemy dostęp. Każdy zasób na serwerze powinien posiadać indywidualny identyfikator URI. Dzięki temu nasze API jest lepsze od poziomu 0, gdzie mieliśmy pojedynczy identyfikator URI dla każdego żądania od klienta. Warto pamiętać, ze zasób nie powinien być dostępny pod wieloma adresami, tz. musi mieć unikalny adres.

Przykład API na poziomie 1:

POST api/persons/create
POST api/persons/id/get
POST api/persons/id/update
POST api/persons/id/remove

Level Two: HTTP Verbs (Poziom 2)

Na drugim poziomie modelu dojrzałości Richardsona dla protokołu HTTP nadajemy semantykę. Do implementacji RESTful API  będziemy wykorzystywać przede wszystkim metody POST, GET, PUT, PATCH i DELETE zgodnie z ich przeznaczeniem.

Przykład API na poziomie 2:

POST /api/persons
GET /api/persons/id
PUT /api/persons/id
PATCH /api/persons/id
DELETE /api/persons/id

W wyniku powyższych żądań nie powinniśmy zwracać tylko kodu odpowiedzi 200 (OK), jak to było na niższych poziomach. Na drugim poziomie modelu, API będzie zwracać status odpowiedzi zależnie od sposobu realizacji zapytania. Wyróżniamy następujące grupy kodów odpowiedzi HTTP:

  • 1XX – Kody informacyjne
  • 2XX – Kody powodzenia
  • 3XX – Kody przekierowania
  • 4XX – Kody błędu aplikacji klienta
  • 5XX – Kody błędu serwera

Przykład dodawania osoby:

POST /api/persons HTTP/1.1
{ "name":"Patryk Sładek", "age":27 }

Przykładowe odpowiedzi:

HTTP/1.1 201 Created
{ "id":36 }

lub

HTTP/1.1 409 Conflict
{ "error":"Person with name Patryk Sładek already exists." }

Level Three: Hypermedia Controls (Poziom 3)

Trzeci poziom (najwyższy) RMM wymaga implementacji HATEOAS (Hypertext As The Engine Of Application State). W wielu firmach trzeci poziom modelu nie jest realizowany, ze względu na dodatkową złożoność API. Firmy godzą się, z tym że ich API znajduje się na 2 poziomie, tym samy nie jest to RESTful API. HATEOAS kieruje interakcją dla klienta. Klient API nie zna wszystkich punktów końcowych API, dla których może wykonać żądanie.

Przeanalizujmy następujący przykład. Z dokumentacji API wiemy, pod jakim adresem URI możemy uzyskać osobę o danym identyfikatorze. Wykonujemy żądanie GET, w wyniku którego otrzymujemy strukturę z danymi identyfikującymi osobę. Stosując HATEOAS dla pola identyfikującego adres osoby, zamiast id adresu otrzymamy adres URI do dodatkowych informacji dla powyższego zasobu. Przechodząc pod wybrany adres URI osoby, uzyskamy szczegółowe dane, gdzie np. zamiast identyfikatora adresu osoby otrzymamy link do zasobu reprezentującego adres. Wykorzystując HATEOAS dostarczamy klientowi linki do poruszania się po API. Zastosowanie HATEOAS minimalizuje nam dokumentacje API. Usługa zaimplementowana na poziomie trzecim jest uważana za RESTful API.

Przykład pobierania osoby o identyfikatorze 36:

GET /api/persons/36 HTTP/1.1

Przykładowa odpowiedź:

HTTP/1.1 200 OK
{ "id":36, "name":"Patryk Sładek", "age":27,
  "link":{"address":"/api/addresses/29"} }