4. října 2012

Architektonické principy RESTu

Jedním ze zdrojů, který jsem použil jako přípravu na certifikaci Java EE 6 Web Services Developer (o které jsem psal nedávno), je kniha RESTful Java with JAX-RS od Billa Burkeho. Bill je určitě vhodným autorem, poněvadž byl/je leaderem projektu RESTEasy od JBossu, což je certifikovaná implementace JAX-RS.

Co se týká knihy, můžu ji doporučit. Je dobře napsaná, Bill nijak netlačí svoje RESTEasy, ale objektivně zmiňuje i další alternativy - referenční implementaci Jersey a Apache CXF. Mě osobně trochu mrzelo, že celá druhá polovina knihy je workbook, jakýsi guide k příkladovému projektu - přece jenom, jak se pohybuju v architektonických sférách, tak to pro mne není až tak přínosný. Nicméně někomu jinému může tato "konrétně implementační" část pomoci dostat se (rychleji) do problému.

Ohledně RESTu jsem byl, až do přečtení knihy, panensky nepoznamenán. Teoreticky již nyní mám načteno (i z jiných zdrojů), ovšem RESTový projekt, který by mne pokřtil ohněm, mě teprve čeká. (To že jsem jednou restovou architekturu navrhl v nabídce, se nepočítá) V tomto postu (aby mi něco z toho v hlavě zůstalo) bych se zaměřil na to, co pro mne bylo nejpřínosnější a nejzajímavější: zcela jednoznačně to jsou architektonické principy RESTu.

Že termín representational state transfer pochází z dizertačky Roye Fieldinga (2000), bylo na webu napsáno již milionkrát, takže to tady nebudu omílat a skočím rovnou rovnýma nohama do principů, na kterých stojí.

Adresovatelnost

Celý REST se točí kolem zdrojů (resources). Každý takový zdroj by měl být dosažitelný pomocí jedinečného identifikátoru (URI). Tohle je celkem jednoznačný u dokumentů, aplikací a služeb. Ale když vezmeme v potaz např. Java objekty deployované na aplikačním serveru, tak u nich chybí jednotný/jednoznačný způsob identifikace, který se navíc bude lišit podle různých implementací/dodavatelů (např. JNDI).

Pro identifikaci zdroje se používá URI, které má standardizovanou podobu:

  • scheme://host:port/path?queryString#fragment

přičemž scheme je protokol (např. HTTP), host je DNS nebo IP adresa, port je... port, path je virtuální hierarchická struktura, queryString je množina párů klíč=hodnota, oddělených znakem & a fragment je identifikátor, který označuje nějakou část zdroje (např. nadpis nebo obrázek v dokumentu).

Jednotné rozhraní

Tak jako se REST točí kolem zdrojů, tak se také točí kolem HTTP, jehož jednotlivý metodám připisuje specifický účel a význam.

  • GET je určena pro read-only operace, jako je načtení seznamu zdrojů (dokumentů), nebo obsahu jednoho zdroje. Je jednak idempotentní (lze ji volat opakovaně se stále stejným výsledkem) a jednak bezpečná (safe, nezpůsobuje žádné vedlejší efekty).
  • PUT umožní daný resource (tělo zprávy/requestu) uložit na serveru na určitém URL. Předpokládá, že klient zná potřebný identifikátor. Metoda je idempotentní, technicky bývá implementovaná jako insert nebo update.
  • DELETE odstraní daný resource. Je idempotentní.
  • POST je funkčně hodně podobná metodě PUT. Zásadní rozdíl je, že je to jediná metoda která není idemopotentní. Čili s každým dalším requestem měním stav daného zdroje/služby.
  • HEAD je podobná metodě GET, s tím rozdílem, že response nevrací body, ale pouze response code a HTTP hlavičky.
  • OPTIONS vrací komunikační možnosti a schopnosti serveru pro dané URI.

Orientace na reprezentaci

Při komunikaci pomocí HTTP metod dostáváme a posíláme na dané URI nějakou reprezentaci zdroje. Podle toho, čím je daný zdroj reprezentován, tak dostáváme/posíláme XML, JSON, YAML apod.

Metodou GET například můžu z nějakého URI dostat takovouto reprezentaci:
<product id="42">
    <name>Kindle Touch</name>
    <currency>USD</currency>
    <price>139</price>
</product>
Pokud bych chtěl zdroj na serveru změnit, pak pošlu metodou PUT (nebo POST) na stejné URI následující reprezentaci (změna ceny):
<product id="42">
    <name>Kindle Touch</name>
    <currency>USD</currency>
    <price>99</price>
</product>
Jestli bych použil PUT nebo POST, by záleželo na designu této služby. Obecně, protože PUT je, podle definice, idempotentní, tak se používá pro update, POST idempotentní není a používá se pro insert.

Nebo jinak definováno - s prvním PUT requestem se založí na serveru zdroj a s každým dalším stejným PUT requestem se tento zdroj aktualizuje. Pokud request obsahuje stejná data, zdroj se nijak nemění (to je ta idempotentnost). Naopak s každým POST requestem by měl na serveru vždy vzniknout nový zdroj. To v reálu často nebývá pravda - POST funguje jako update (stále stejného zdroje), což ale není čistý RESTový design.

Bezstavová komunikace

Bezstavovost znamená, že žádná klientská data nejsou spravována na serveru, tzn. žádná klientská session. Pokud aplikace potřebuje udržovat stavovost, spravuje si ji sama (jako kdyby to byl tlustý klient) a pouze propaguje na server změněné zdroje.

Mezi běžně uváděné benefity bezstavovosti patří: jednoduchost business logiky, škálovatelnost a cacheování resourců.

HATEOAS

Význam této ošklivé zkratky je Hypermedia As The Engine Of Application State. S pochopením tohoto principu jsem měl největší problém. Myšlenka je jednoduchá - datový formát, hypermedia (rošíření hypertextu), poskytuje speciální informaci, jak měnit stav zdroje. A dotaženo do konce - klient by měl komunikovat se serverem pouze pomocí hypermedia a nepotřebuje k tomu žádnou další (technologickou) informaci.

V případě webových služeb je HATEOAS přítomen v podobě odkazů uvnitř (XML/JSON) dokumentů, které představují zdroje. U XML dokumentů se pro odkazy často používá specifikace Atom a z ní konkrétně element link.
<product id="42">
    <link rel="self"
          href="http://amazon.com/product/42"
          type="application/xml"/>
    <link rel="payment"
          href="http://amazon.com/checkout/42"
          type="application/xml"/>
    <name>Kindle Touch</name>
    <currency>USD</currency>
    <price>139</price>
</product>
Jak pojmenovávat jednotlivé relace uvnitř odkazů je částečně standardizováno, takže se používají např. následující (samopopisná) jména: chapter, edit, first, icon, last, next, payment, previous, self, stylesheet ad.

Zamyšlení

Musím přiznat, že v rámci teoretického načtení, se mi RESTová architektura hodně líbí, zejména svojí čistotou a jednoduchostí. Je otázka (času), jak se tento můj pohled změní při použití RESTu v praxi. Tu praxi, bohužel, zatím vidím na nějaký menší projektík, protože jak se poslední dobou věnuju SOA v enterprise sféře, tak musím konstatovat, že REST je v této oblasti opomíjen jak dodavateli řešení, tak zákazníky. Ale snad se časem začne blýskat na lepší časy.

9 komentářů:

  1. Výhodu RESTu vidím třeba i v tom, že se GETy (read-only operace) dají cachovat.

    OdpovědětVymazat
    Odpovědi
    1. Jo, jo. Je to v sekci Bezstavová komunikace.

      Vymazat
    2. Vyhodou restu je hlavne to ze je to obecne uznavanej standard. Od toho se odviji vsechno ostatni vcetne cachovani HTTP.

      Vymazat
  2. REST bohužel není vhodný pro všechna řešení, určitě se nalezne mnoho systémů, kde je mnohem praktičtější zvolit SOAP. Nicméně stále tak nějak optimisticky věřím, že například mnoho informačních systémů a datově orientovaných aplikací by mohlo být velmi čistě dekomponováno na zdroje. :)

    Není úplně správné označovat schéma URI za protokol, je mnoho používaných schémat, které protokoly nejsou, např. data či geo. Ale na druhou stranu je pravda, že mnoho RFC standardů obsahuje tyto chyby a nesrovnalosti.

    OdpovědětVymazat
  3. Skvely clanky, diky!

    OdpovědětVymazat
  4. Článek je přínosný, ale pozor na /> v poslední ukázce kódu za atributem href, to tam určitě být nemá :)

    OdpovědětVymazat