Sytuacja kobiet w IT w 2024 roku
5.02.20215 min
Kamil Kiełbasa

Kamil KiełbasaSoftware Engineer / Blogger / TrainerExeno

Dlaczego Twoje API nie jest RESTful?

Zobacz, dlaczego zaimplementowanie metody GET, POST, PUT i DELETE dla danego zasobu nie wystarczy, żeby stworzyć piękne API RESTowe.

Dlaczego Twoje API nie jest RESTful?

Za górami, za lasami, kiedy JSON był tylko marzeniami, powstał twór wspaniały. Architektoniczny styl, który zwojował świat swoją prostotą, jak i elastycznością. Mowa tutaj oczywiście o RESTful API. Jako że jest to nadal bardzo często spotykane rozwiązanie, chciałbym Wam opowiedzieć o kilku problemach, mitach, a przede wszystkich o tym, kiedy API wystawione w formacie JSON nie jest RESTful.

Krótka historia RESTful API

Zacznijmy więc od krótkiego przedstawienia, jak powstał ten termin. Dawno dawno temu, w epoce, kiedy zamiast przez smartphona, ludzie rozmawiali, spotykając się ze sobą, człowiek imieniem Roy Fielding, napisał pracę doktorską, zatytułowaną Architectural Styles and the Design of Network-based Software Architectures to właśnie w tej pracy po raz pierwszy pojawił się termin “Representational state transfer“.

Teraz pierwsza niespodzianka: praca Roy'a powstała w roku 2000. A czy ktoś wie, kiedy powstał format wymiany danych JSON? Niestety sam nie jestem Wam w stanie podać pełnej daty, ponieważ angielska Wikipedia pisze, że Douglas Crockford opracował JSON-a na początku lat 2000. Idąc dalej tym tropem, możemy się dowiedzieć, że Douglas dopiero w roku 2002 zdobył prawa do domeny json.org, a dopiero w 2006 powstał pierwszy standard RFC 4627. Co dość dobrze obrazuje fakt, że sam JSON nie jest wymagany do stworzenia resftul API.

Czy można stworzyć takie API w innym formacie danych, np. w kochanym przez wszystkich XML-u? Oczywiście, że tak! Jeżeli XML lub inny format danych bardziej spełnia Wasze wymagania biznesowe - to tak.

RESTful API nie ogranicza się tylko do JSON-a.

Skoro mamy to uzgodnione, to przejdźmy do kolejnych rzeczy.

Stateless

Wiele systemów, z którymi miałem okazję pracować, bardzo lubiło przetrzymywać część informacji w sesji. Inne potrafiły przetrzymywać bardzo dużo elementów w pamięci, tak, aby wykorzystać je przy następnym przychodzącym żądaniu do serwera. To prawda, że takie mechanizmy mają swoje zalety, lecz powodują też, że Twoje API przestaje być RESTful. Dlaczego? Powód jest bardzo prosty: cała reprezentacja stanu obiektu lub zasobu powinna być przesyłana. Tak więc, jeśli przesyłamy całość stanu, to po co nam sesja lub mechanizm cache'a w pamięci.

RESTful API jest bezstanowe.

Cache

No to jak już nawiązaliśmy do cache'a, możemy pójść dalej. Pewnie nie raz korzystaliście z możliwości obecnych przeglądarek internetowych do cache'owania zasobów. Czy to były skrypty, CSS-y, obrazki, możliwe też, że cache'owaliście odpowiedzi requestów. Oczywiście RESTful API nie definiuje nam, jak mamy ściągać takie zasoby, jak obrazki, CSS-y etc., a dodaje jedną bardzo ważną zasadę: serwer powinien pośrednio lub bezpośrednio powiedzieć klientowi, że dany zasób może sobie zcache'ować.

Serwer pośrednio lub bezpośrednio informuje klienta o możliwości zcache'owania zasobu.

Uniform Interface

Płynnie przechodząc do innego elementu RESTowego API, krótko omówimy “ujednolicony interfejs”. Nazewnictwo i odpowiednia identyfikacja zasobów są kluczowym elementem tego typu API. Według definicji mamy 4 ograniczenia, które pomagają nam uprościć architekturę:

Resource identification in requests

Na podstawie pojedynczego requestu, serwer może zidentyfikować zasób, którego dotyczy żądanie. Najczęściej stosowany jest do tego adres URI. Dodatkowo zwracany zasób nie powinien być zależny od jego reprezentacji. W prostych słowach - na odpowiedź serwera nie powinien mieć wpływu format danych.

Resource manipulation through representations

Pobranie zasobów wraz z jego metadanymi powinno posiadać wystarczająco dużo informacji, aby móc go modyfikować lub usunąć.

Self-descriptive messages

Wiadomość powinna zawierać wystarczającą ilość informacji, aby serwer mógł ją przeprocesować. Najlepszym przykładem jest ustawianie metadanych jak Content-Type w nagłówkach żądania HTTP. Wtedy serwer może użyć odpowiedniego parsera (json, xml, html etc.), aby otrzymać obiekt z wiadomości.

Hypermedia as the engine of application state (HATEOAS)

Moim zdaniem to najfajniejszy podpunkt do implementacji. Pozwala przyrównać RESTful API do strony internetowej. Ponieważ strona to zbiór linków prowadzących do kolejnych podstron, HATEOAS informuje nas, że to samo można zrobić ze swoim API. Najlepiej zobrazować to na przykładzie Graph API od Facebooka. Przedstawiam przykładową odpowiedź z ichniejszej dokumentacji:

{
  "feed": {
    "data": [
      {
        "created_time": "2017-12-12T01:24:21+0000",
        "message": "This picture of my grandson with Santa screams Coca Cola",
        "id": "820882001277849_1809387339093972"
      },
      {
        "created_time": "2017-12-11T23:40:17+0000",
        "message": ":)",
        "id": "820882001277849_1809316002434439"
      },
      {
        "created_time": "2017-12-11T23:31:38+0000",
        "message": "Thought you might enjoy this.  My horse loves Coke!",
        "id": "820882001277849_1809310929101613"
      }
    ],
    "paging": {
      "cursors": {
        "before": "Q2c4U1pXNTBYM0YxWlhKNVgzTjBiM0o1WDJsa0R5UTRNakE0T0RJd01ERXlOemM0TkRrNkxUVXdPRE16TXpVM01EQXpNVFUwTkRRME5Ua1BER0ZA3YVY5emRHOXllVjlwWkE4ZA09ESXdPRGd5TURBeE1qYzNPRFE1WHpFNE1Ea3pPRGN6TXprd09UTTVOeklQQkhScGJXVUdXaTh2eFFFPQZDZD",
        "after": "Q2c4U1pXNTBYM0YxWlhKNVgzTjBiM0o1WDJsa0R5TTRNakE0T0RJd01ERXlOemM0TkRrNk1UTTJORE01T0RVNU1UZAzVPRGMyTnpFNE1BOE1ZAWEJwWDNOMGIzSjVYMmxrRHlBNE1qQTRPREl3TURFeU56YzRORGxmTVRnd09USXdOamsxTlRjM09EWTNOdzhFZAEdsdFpRWmFMdk9HQVE9PQZDZD"
      },
      "next": "https://graph.facebook.com/820882001277849/feed?access_token=valid_token_goes_here"
    }
  },
  "id": "820882001277849"
}


Jak spojrzymy w sekcję paging, pod kluczem next zobaczymy link, który poprowadzi nas do kolejnych wyników. Tak właśnie działają hypermedia.

Inne często spotykane nieścisłości

Oczywiście, to nie wszystkie nieścisłości i niuanse pomijane przy implementacji RESTful API. Często spotykałem się z sytuacją, gdzie żądanie typu GET, zmieniało stan systemu, co jest całkowicie niezgodne ze specyfikacją RESTful API (jak i z dobrymi praktykami). Rozumiem, że zdarza się, że do takiego żądania chcemy podpiąć np. licznik ściągnięć danego zasobu, ewentualnie monitoring wydajności, ale proszę - nie róbcie czegoś w stylu:

GET: https://example.com/resource/1/votes/add/5

Nie jest to poprawną implementacją architektury Representational State Transfer. Taki API osobiście lubię nazywać “JSON over HTTP”, ponieważ zazwyczaj z RESTem to nie ma zbyt wiele wspólnego.

Podsumowanie

Mam nadzieje, że przybliżyłem Wam nieco zasady RESTowego API. Sam przez wiele lat uważałem, że wystarczy zaimplementować metody GET / POST / PUT / DELETE dla danego zasobu i już jest gotowe piękne API RESTowe. Niestety dopiero po pewnym czasie uświadomiłem sobie, jak bardzo ta kwestia jest spłycana. Coś jak z GIT-em: No tutaj zrobisz pulla, walniesz kilka zmian, zrobisz pusha i zobaczysz, że więcej Ci do szczęścia nie potrzeba”...

Na koniec chciałbym, żebyście zapamiętali, iż nie każde API z wykorzystaniem JSON-a to REST, tak jak nie każdy REST jest zbudowany wokół JSON-a.

<p>Loading...</p>