data.mdx.frontmatter.hero_image

Dlaczego Twoje API nie jest RESTful

2018-12-14 | Bez Kategorii | bd90

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 smartphone-a ludzie rozmawiali i spotykali 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, ktoś wie kiedy powstał format wymiany danych JSON? Niestety sam nie jestem wam wstanie 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 że sam JSON nie jest wymagany do stworzenia RESTfull 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.

REST-ful API nie ogranicza się tylko do JSON-a

Skoro mamy to uzgodnione to przejdźmy do innych 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 request-cie do serwera. Nie powiem, takie mechanizmy mają swoje zalety, lecz powodują, że Twoje API przestaje być RESTful. Dlaczego? Powód jest bardzo prosty: cała reprezentacja stanu obiektu / zasobu powinna być przesyłana. Więc jeśli przesyłamy całość stanu to po co nam sesja czy mechanizm cache-a w pamięci.

Rest-ful API jest bez stanowe

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 REST-ful 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 zcacheowania zasobu

Uniform Interface

Płynnie przechodząc do innego elementu REST-owego 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 request-u 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 powinno mieć wpływu format danych)

Resource manipulation through representations

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

Self-descriptive messages

Wiadomość powinna zawierać wystarczającą ilość informacji, aby serwer mógł ją prze procesować. 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 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 / niuasne pomijane przy implementacji "RESTful API". Często spotykałem się z sytuacją, gdzie żądanie typu GET zmieniało stan systemu, co jest całkowicie nie zgodne z specyfikacją RESTfull 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 REST-em to nie ma zbyt wiele wspólnego.

Podsumowanie

Mam nadzieje, że przybliżyłem wam nieco zasady REST-owego 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 REST-owe. Niestety dopiero po pewnym czasie uświadomiłem sobie jak bardzo ta kwestia jest spłycana, coś jak z GIT-em: "No tutaj zrobisz pull-a, walniesz kilka zmian zrobisz push-a i zobaczysz że więcej Ci do szczęścia nie potrzeba" :)

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

Mam nadzieje że tekst się podobał :)

Jak zwykle dzięki za przeczytanie!

Do Następnego!

Cześć

By Bd90 | 14-12-2018 | Bez Kategorii