HATEOAS - wady, zalety, szanse, zagrożenia, fakty, mity

4

Szkoda by potencjalnie ciekawa dyskusja była rozsmarowana w komentarzach w wątku Zbyt wiele typów w jednej metodzie.
Temat zasługuje na osobny wątek, w którym może się wywiąże dyskusja, a może nie ;)

Na technologię staram się patrzeć przede wszystkim przez pryzmat tego jaki problem rozwiązuje (i jakie generuje). Osobiście w HATEOAS nie wiedzę wartości, bo pewnie nie zetknąłem się z problemami, dla których HATEOAS byłby odpowiedzią.

  1. Czy ktoś widzi wartość z wykorzystania HATEOAS dla konkretnego problemu i może się podzielić wiedzą co to za problem i jak HATEOAS pomaga?
  2. Często ludzie piszą o "pozmieniały się linki na serwerze" i HATEOAS jest rozwiązaniem, ale czy odpowiedzią nie jest też HTTP/301? Swoją drogą, dlaczego linki miałyby się często zmieniać?
2

Nie piszę w tych kobyłach javowych, ale to pytanie:

Swoją drogą, dlaczego linki miałyby się często zmieniać?

Przypomina mi o tym:)
http://slinkp.com/falsehoods-programmers-believe-about-apis.html

8

HATEOAS teoretycznie ma zapewnić ewolucyjność naszemu API zamiast wersjonowania. Znamy jeden endpoint, a później nasz client zamiast mieć zahardkodowane ścieżki do pozostałych endpointów to ma podążać dalej zwracaniami linkami. HATEOAS ma też spełnić wymaganie samoopisujacych się wiadomości narzuconych z RESTA, czyli dostając wiadomość wiemy gdzie dalej możemy iść.

W praktyce wersjonowanie i dokumentacja endpointów są znacznie prostsze w implementacji i utrzymaniu, niż zabawa w hejteosy.

2

Ideą HATEOAS jest to by API było "przeglądalne" na podobnej zasadzie jak HTML, tylko dla maszyn. Czyli np. jak masz dokument JSONa w postaci:

{
  "name": "Tesla 3"
}

Oraz dokument w postaci:

{
  "name": "Jan Nowak"
}

To człowiek "widzi", że to są zupełnie różne rzeczy (jedno opisuje samochód, drugie człowieka). Ideą HATEOAS jest dodanie kontekstu do danych pól, w ten sposób jeśli zapiszemy:

{
  "@context": "https://schema.org",
  "@type": "Car",
  "name": "Tesla 3"
}

oraz

{
  "@context": "https://schema.org",
  "@type": "Person",
  "name": "Jan Nowak"
}

Wtedy maszyna też może wiedzieć co oznaczają odpowiednie pola, nawet jak otrzyma odpowiedź z serwisu, którego nie zna. Dodatkową rzeczą jest to, że używając JSON-LD/HAL możesz umieścić dodatkowe linki. Więc np. możesz "łatwo" znaleźć link do tego by znaleźć autora posta jeśli poprzez API pobierzesz opis posta, bo może to być w postaci:

{
  "@context":"http://schema.org",
  "@type":"DiscussionForumPosting",
  "author": {
    "@type": "Person",
    "@id": "https://4programmers.net/Profile/27582",
    "name": "Hauleth"
  },
  "content": "Ideą HATEOAS jest to by API było "przeglądalne" na podobnej zasadzie jak HTML…"
}

W ten sposób niejako "automatycznie" wiesz skąd pobrać więcej danych nt. autora jeśli potrzebujesz.


EDIT: Jeśli to nie jest z tekstu powyżej oczywiste - HATEOAS nie ma służyć jako "dokumentacja" czy "API do wykrywania linków", ale ma nadawać kontekst i sens polom w zwróconych danych (czy to JSON czy XML nie ma specjalnie znaczenia).

1

@hauleth: czyli wygląda na to, że HATEOAS potrafi rozwiązać problemy ludzi, którzy porzucili XML + XSD Schema i np. SOAP, na rzecz nowej/lepszej/prostszej technologii (REST/JSON), a później odkryli pewne braki nowej technologii.

2

Nie, bo odpowiednikiem XSD Schema jest JSON Schema, nie HATEOAS. HATEOAS dotyczy w równym stopniu jakichkolwiek danych zwracanych przez serwis webowy (masz Microdata czy RDFa dla XMLa/HTMLa oraz JSON-LD dla JSONa). SOAP to jeszcze zupełnie inna para kaloszy, bo to jest protokół RPC oparty o XMLa. Jeśli by szukać odpowiedników JSON/XML:

XML JSON Zastosowanie
XSD/DTD JSON Schema Opis formatu dokumentu
Microdata JSON-LD Nadanie kontekstu wartościom w dokumencie (HATEOAS)
SOAP GraphQL RPC
WADL OpenAPI API Description Language
0
neves napisał(a):

HATEOAS teoretycznie ma zapewnić ewolucyjność naszemu API zamiast wersjonowania. Znamy jeden endpoint, a później nasz client zamiast mieć zahardkodowane ścieżki do pozostałych endpointów to ma podążać dalej zwracaniami linkami.

Co do tego pełna zgoda. Może nawet jakichś aspektów security można się doszukiwać, np. generowanie linków, które są ważne tylko na czas sesji i nawigowanie po takich linkach w ramach sesji.

HATEOAS ma też spełnić wymaganie samoopisujacych się wiadomości narzuconych z RESTA, czyli dostając wiadomość wiemy gdzie dalej możemy iść.

Ale może się okazać, że dostajemy nowego linka i nie wiemy co to znaczy pójść dalej za tym linkiem, bo mógł się pojawić nowy typ czegoś (wspomniane przez @hauleth przykład z name i kontekstem Person/Car - pojawia się nowy kontekst Hotel i linki prowadzące do rezerwacji/anulacji itd.), więc to, że komunikat będzie się samoopisywał, wiele nie zmieni, bo i tak klient będzie musiał te zmiany zrozumieć (chyba, że nie będzie zainteresowany). Osobiście szedłbym w wersjonowanie API.

0

więc to, że komunikat będzie się samoopisywał, wiele nie zmieni, bo i tak klient będzie musiał te zmiany zrozumieć (chyba, że nie będzie zainteresowany)

Tak, ale tutaj chodzi, że masz dokumenty "well-known", które opisują dane pola (np. JSON-LD i Microdata używające Schema.org jest rozumiane przez GMaila). Więc zawsze w ostateczności musisz umieć zrozumieć "coś", ale zdecydowanie łatwiej "nauczyć" program tylko paru kontekstów, które potem będzie potrzebował, a następnie już niejako "automatycznie rozumiesz" wszystko co Ci ludzie wysyłają, bo masz pewne "zasady" jak sprowadzić to do wspólnego mianownika co można zobaczyć w JSON-LD playground.

Ja w swoim side-projekcie chcę użyć JSON-LD dla wystawianego przeze mnie API właśnie z powodu "nadawania kontekstu", a nie jako dokumentacja, od tego będzie OpenAPI Schema.

0
yarel napisał(a):

@hauleth: czyli wygląda na to, że HATEOAS potrafi rozwiązać problemy ludzi, którzy porzucili XML + XSD Schema i np. SOAP, na rzecz NOWEJ (tak) / "lepszej" / prostszej technologii (REST/JSON), a później odkryli pewne braki nowej technologii.

Rzadko się przerabia cytat z poprzednika, pozwoliłem sobie podkreślić NOWEJ (również w sensie mody) i dodać cudzysłów do "lepszej". Nie zgadzam się na bezwarunkowe uznanie REST za lepszą, choć są sytuacje że REST jest lepszy.
Dodam, dla mnie jest ważny "niezgwałcony" REST, tj dotyczący tylko stanu (oczywiście da się go zmasakrować i wykonać procedury), bezstanowy (a co, gdy wiele sytuacji biznesowych jest sesyjne?)
https://pl.wikipedia.org/wiki/Representational_state_transfer

hauleth napisał(a):

Nie, bo odpowiednikiem XSD Schema jest JSON Schema, nie HATEOAS. HATEOAS dotyczy w równym stopniu jakichkolwiek danych zwracanych przez serwis webowy (masz Microdata czy RDFa dla XMLa/HTMLa oraz JSON-LD dla JSONa). SOAP to jeszcze zupełnie inna para kaloszy, bo to jest protokół RPC oparty o XMLa. Jeśli by szukać odpowiedników JSON/XML:

Zainteresował mnie wątek.

  1. Dodał bym, że implementacje (wzorca ??? tfu tfu, znów wzorce) RPC w bardzo ciekawy sposób żyją i rozwijają się, ku niewiedzy programistów ulegających modzie. Np nowoczesne RPC są znacznie lżejsze, szybsze, przygotowane na ewolucyjne niejednoczesne zmiany wersji protokołu po obu stronach "rury", czego "stare RPC" nie miały zrobionego (zbyt dobrze lub wcale).
    Moje własne testy nieoficjalnie wskazały, że Apache Thrift jest rzędu 10x szybszy niż siostrzana co do logiki implementacja w WSDL/SOAP (liczba czysto amatorsko zmierzona na normalnych komputerach). Na realnych z rynku tabletach przebitka była jeszcze większa.

  2. JSON Schema nie w pełni leczy praktyczne problemy wdrożeń (REST)/JSON, bo udokumentować by trzeba było (dla mnie naruszenia) REST-a jak udawanie stateless, gdy w rzeczywistości jest statefull oraz inne.

0
hauleth napisał(a):

więc to, że komunikat będzie się samoopisywał, wiele nie zmieni, bo i tak klient będzie musiał te zmiany zrozumieć (chyba, że nie będzie zainteresowany)

Tak, ale tutaj chodzi, że masz dokumenty "well-known", które opisują dane pola (np. JSON-LD i Microdata używające Schema.org jest [rozumiane przez GMaila]

Dopuszczam możliwość, że nie rozumiem idei HATEOAS, ale może porównanie do czegoś znajomego ułatwi zmianę spojrzenia. Wydaje mi się jednak, że to "well known" to już dawno było w XML/XSD.

  • w dokumencie XSD mam oprócz opisu struktury dokumentu, także zdefiniowany kontekst (różne typy i atrybuty dla elementów dokumentu XML, np. typ Person, Car itd.)
  • w dokumencie XML mam treść dokumentu, która może mieć nadane znaczenie poprzez referencję do jakiegoś schematu (bądź wielu schematów i organizację tej wielości w przestrzeniach nazw)
    Dzięki temu można łączyć różne konteksty w ramach jednego wywołania.

To wszystko operuje na poziomie typów, więc są to "dobrze znane" konteksty/typy/elementy, które można oprogramować.

Różnica w stosunku do HATEOAS jest taka, że ten pozwala zejść na poziom instancji i tam na poziomie instancji widać co można, a czego nie można. Część rzeczy aplikacja rozumie, bo w momencie tworzenia tejże kontekst był "dobrze znany". Części aplikacja nie rozumie (bo pojawił się upgrade API po stronie serwera). I czy tu HATEOAS czy inne rozwiązanie, to problem zaadresowania "delty" pozostanie.

Czy jest jakaś wartość w tym, że znamy to co wolno na poziomie instancji (w jakimś konkretnym jej momencie cyklu życia), mimo że części z tych rzeczy, które wolno wykonać, to możemy nie rozumieć?

0
neves napisał(a):

... czyli dostając wiadomość wiemy gdzie dalej możemy iść.

Zwróciliście uwagę, ze to scenariusz statefull ?

1

Mój pogląd na to:

  1. Czy ktoś widzi wartość z wykorzystania HATEOAS dla konkretnego problemu i może się podzielić wiedzą co to za problem i jak HATEOAS pomaga?

Wystawiasz publiczne API, przez Hejtoasa określasz, jakie operacje "biznesowe" może wykonać klient. Jest to swoiste mapowanie akcja biznesowa -> konkretny endpoint. Dzięki temu mapowaniu możesz zmienić endpointy, a klient dalej będzie działał, o ile zapiął się na klucze.

  1. Często ludzie piszą o "pozmieniały się linki na serwerze" i HATEOAS jest rozwiązaniem, ale czy odpowiedzią nie jest też HTTP/301? Swoją drogą, dlaczego linki miałyby się często zmieniać?

Musisz mieć klienta, który ogarnia redirecty + niepotrzebnie zwiększasz ruch. Zmiana linków nie jest OK, rozwiązaniem jest wprowadzanie kompatybilnych zmian lub stopniowa migracja klientów na nową wersję API.

5

Alternatywą, zresztą chyba najpopularniejszą w przypadku "wewnętrznych API", jest zwyczajnie dostarczanie libki z klientem do danego serwisu, która zajmuje się ogarnianiem endpointów i payloadów, a wystawia "biznesowe" API.
W publicznym API jest to o tyle problem, że dla ilu języków ktoś takie libki by robił :)

0

Dopuszczam możliwość, że nie rozumiem idei HATEOAS, ale może porównanie do czegoś znajomego ułatwi zmianę spojrzenia. Wydaje mi się jednak, że to "well known" to już dawno było w XML/XSD.

Tak, ale to dalej nie to samo. Zdanie Zamek jest czarny. jest poprawne składniowo, tak samo dokument <entry><what>Zamek</what><colour>Black</colour></entry>. Oba są poprawne (wg zadanej składni), ale w obu przypadkach nie mamy znaczenia słowa zamek. XSD jest zupełnie niezależne od HATEOAS, bo XSD opisuje składnię, a nie znaczenie poszczególnych elementów. Rozumiesz co mam na myśli?

Bardzo dobrze to widać w przykładach na <Schema.org>. Przykładowo mając taki dokument HTML:

<div class="event-wrapper">
  <div class="event-date">Sat Sep 14</div>
  <div class="event-title">Typhoon with Radiation City</div>
  <div class="event-venue">
    The Hi-Dive
    <div class="address">
      7 S. Broadway<br>
      Denver, CO 80209
    </div>
  </div>
  <div class="event-time">9:30 PM</div>
  <div class="event-price">$13.00</div>
  <a href="http://www.ticketfly.com/purchase/309433">Tickets</a>
</div>

Dla człowieka jest jasne, że to opisuje wydarzenie "Typhoon with Radiation City" dziejące się w Denver o 21:30 gdzie bilet kosztuje $13. Jednak maszyna może co najwyżej zgadywać jaki jest sens. Natomiast jeśli dodamy (do tego samego, poprawnego dokumentu) Microdata:

<div class="event-wrapper" itemscope itemtype="http://schema.org/Event">
    <div class="event-date" itemprop="startDate" content="2013-09-14T21:30">
        Sat Sep 14
    </div>
    <div class="event-title" itemprop="name">
        Typhoon with Radiation City
    </div>
    <div class="event-venue" itemprop="location" itemscope itemtype="http://schema.org/Place">
        <span itemprop="name">The Hi-Dive</span>
        <div class="address" itemprop="address" itemscope itemtype="http://schema.org/PostalAddress">
            <span itemprop="streetAddress">7 S. Broadway</span><br>
            <span itemprop="addressLocality">Denver</span>, <span itemprop="addressRegion">CO</span> <span itemprop="postalCode">80209</span>
        </div>
    </div>
    <div class="event-time">9:30 PM</div>
    <div itemprop="offers" itemscope itemtype="http://schema.org/Offer">
        <div class="event-price" itemprop="price" content="13.00">$13.00</div>
        <meta itemprop="priceCurrency" content="USD"><a itemprop="url" href="http://www.ticketfly.com/purchase/309433">Tickets</a>
    </div>
</div>

To maszyna (która zna RDF w postaci Microdata oraz http://schema.org/Event) może bez większego problemu dojść, który element na stronie oznacza co. Dzięki temu de facto można używać tego samego endpointu do serwowania HTMLa, JSONa czy XMLa i we wszystkich formatach zawrzeć te same dane, które będą jasno zdefiniowane dla komputera (i de facto będzie można je stosować zamiennie, nawet jeśli HTML będzie zawierał mnóstwo elementów nie związanych z danymi a przedstawieniem tego na stronie internetowej).

Inaczej mówiąc cała idea HATEOAS jest taka, by komputer był w stanie z HTMLa i JSONa wyciągnąć te same dane i wiedzieć dokładnie co każde z nich oznacza. Przykładowo powyższy dokument HTML w postaci JSONa z LD byłby w postaci:

{
  "@context": "http://schema.org",
  "@type": "Event",
  "location": {
    "@type": "Place",
    "address": {
      "@type": "PostalAddress",
      "addressLocality": "Denver",
      "addressRegion": "CO",
      "postalCode": "80209",
      "streetAddress": "7 S. Broadway"
    },
    "name": "The Hi-Dive"
  },
  "name": "Typhoon with Radiation City",
  "offers": {
    "@type": "Offer",
    "price": "13.00",
    "priceCurrency": "USD",
    "url": "http://www.ticketfly.com/purchase/309433"
  },
  "startDate": "2013-09-14T21:30"
}

Wystawiasz publiczne API, przez Hejtoasa określasz, jakie operacje "biznesowe" może wykonać klient. Jest to swoiste mapowanie akcja biznesowa -> konkretny endpoint. Dzięki temu mapowaniu możesz zmienić endpointy, a klient dalej będzie działał, o ile zapiął się na klucze.

Nie zgodzę się tutaj. Od tego masz dokumentację, nie HATEOAS.

2

HATEOAS to nawet fajna idea, która nie wytrzymała próby implementacji. Tak jak ktoś wcześniej wspomniał, dokumentacja i wersjonowanie jest "łatwiejsze"

0
hauleth napisał(a):

Dopuszczam możliwość, że nie rozumiem idei HATEOAS, ale może porównanie do czegoś znajomego ułatwi zmianę spojrzenia. Wydaje mi się jednak, że to "well known" to już dawno było w XML/XSD.

Tak, ale to dalej nie to samo. Zdanie Zamek jest czarny. jest poprawne składniowo, tak samo dokument <entry><what>Zamek</what><colour>Black</black></entry>. Oba są poprawne (wg zadanej składni), ale w obu przypadkach nie mamy znaczenia słowa zamek. XSD jest zupełnie niezależne od HATEOAS, bo XSD opisuje składnię, a nie znaczenie poszczególnych elementów. Rozumiesz co mam na myśli?

Wydaje mi się, że wiem o co Ci chodzi, ale nie wiem czy w drugą stronę się jasno wyraziłem. Możliwe, że mówimy o tym samym z perspektywy różnych technologii. Możliwe też, że nadal nie wiem co masz na myśli :) Za pomocą XSD można opisać też znaczenie, nie tylko składnię, ale może to moje przeświadczenie?

Dla tego przykładu (zakładam, że podałeś wersję uproszczoną):
<entry><what>Zamek</what><colour>Black</black></entry>

pomijasz element jakim jest odwołanie do schemy, w ramach, której należałoby treść interpretować. Sama treść, bez odwołania do schematu, nie niesie informacji łatwej w przetwarzaniu dla maszyn, ale jak już dodamy schematy, to się zmienia.

(Przykłady pisane z palca, więc mogą mieć różne braki.)

<entry xmlns="http://moje.schematy/budynki/Zamki">
  <what>Zamek</what><colour>Black</colour>
</entry>

<entry xmlns="http://moje.schematy/mechanizmy_bezpieczenstwa/Zamki">
  <what>Zamek</what><colour>Black</colour>
</entry>

Możemy je również mixować i wyrażać sens typu: W czarnym zamku jest złoty zamek, co ludziom może stwarzać problemy, ale maszynom już nie.

<entry xmlns:b="http://moje.schematy/budynki/Zamki"
       xmlns:l="http://moje.schematy/mechanizmy_bezpieczenstwa/Zamki">
  <b:what>Zamek</b:what><b:colour>Black</b:colour>
  <l:what>Zamek</l:what><l:colour>Gold</l:colour>
</entry>

Przykładowych schematów nie generuję, bo nie wnoszą jakieś istotnej wartości dla zilustrowania tego o co mi chodzi z tym nadawaniem znaczenia zawartości treści wyrażonej w dokumencie XML. Jednak tworząc schemat, starałbym się odwzorować biznesowe znaczenie i miał w schemacie typ Zamek, który ma elementy składowe (Wieza, Fosa, Brama etc. wraz z odpowiednimi atrybutami per typ, np. Fosa -> szerokosc, glebokosc ). W schemacie miałbym też kontrolę struktury (krotność, opcjonalność, kolejność etc.). Maszyna znając schemat może sobie spokojnie przetwarzać zgodnie ze schematem, który zna.

Jeśli:

  • z HTMLa wytniemy itemtype="http://schema.org/Event" ,
  • z JSONa wytniemy "@context": "http://schema.org", "@type": "Event", ,
    to pewnie maszyny przestaną rozumieć co jest czym. Tak samo jak pominięcie odwołania do schematu w dokumencie XML.

Inaczej mówiąc cała idea HATEOAS jest taka, by komputer był w stanie z HTMLa i JSONa wyciągnąć te same dane i wiedzieć dokładnie co każde z nich oznacza.

W tym miejscu wypadałoby zadać pytanie, "Dlaczego maszyna, bez konceptu HATEOASa, nie była/jest w stanie z HTML i JSON wyciągnąć danych i rozumieć znaczenia, bez konceptu HATEOASa?" Może powstał w wyniku potrzeb pudrowania braków w architekturach RESTowych? Nie oceniam, że to crap, tylko głośno myślę by sprowokować reakacje :)

Dzięki za podjęcie tematu i wysiłek włożony w dyskusję.

1
yarel napisał(a):

W tym miejscu wypadałoby zadać pytanie, "Dlaczego maszyna, bez konceptu HATEOASa, nie była/jest w stanie z HTML i JSON wyciągnąć danych i rozumieć znaczenia, bez konceptu HATEOASa?" Może powstał w wyniku potrzeb pudrowania braków w architekturach RESTowych? Nie oceniam, że to crap, tylko głośno myślę by sprowokować reakacje :)

Dzięki za podjęcie tematu i wysiłek włożony w dyskusję.

Możesz mieć rację. Pomiędzy bardzo klarowną ideą Fieldinga, a tym jak REST jest masakrowany "w przemyśle", jest wielka różnica,
A bierze się to z przeciążenia go do rzeczy, do których nie jest przeznaczony. Razem z SOAP-em należało w imię mody odrzucić również samą ideę RCP itd...

1 użytkowników online, w tym zalogowanych: 0, gości: 1