Jedno RESTované API a la Restlet, pán hlavný!

Článok bol aktualizovaný 25. 9. 2014 o vyjasnenia a aktualizáciu Restletu na 2.2.2.

Restlet je ľahkotonážny framework pre implementáciu REST architektúry v Jave. Nestrácajme čas vysvetľovaním, čo je REST a prejdime rovno na vec.

V tutoriáli si ukážeme hračkársky príklad zameraný na jeden z mnohých variantov použitia Restletu: bez Mavenu, bežiaceho v Java SE (nie v servletovom kontajneri). Bonusovkou bude použitie JSONu na automatickú serializáciu a deserializáciu objektov.

Prvý projekt

Máme dve možnosti: buď ručne stiahneme projekt, alebo využijeme Maven.

Ručné stiahnutie

  1. Navštívme sekciu pre download na projektovej stránke.

  2. Restlet je k dispozícii v troch branchoch podľa debianovského ducha. Zvoľme si stabilnú vetvu (september 2014: 2.2.2)

  3. K dispozícii je viacero edícií v závislosti od cieľového prostredia (Java SE, Java EE, GAE, GWT, Android a iné). Vyberme si Java SE

  4. Stiahnime podľa ľubovôle buď ZIP archív alebo Windows Installer (ak ich používame)

  5. Rozbaľme archív na vhodné miesto, napr.:

    c:\java\restlet-jse
    
  6. Vyrobme nový projekt v obľúbenom IDE.

  7. Do projektu pridajme org.restlet.jar

Alternatíva: Maven

Pridajme do pom.xml:

<repository>  
   <id>maven-restlet</id>  
   <name>Public online Restlet repository</name>  
   <url>http://maven.restlet.org</url>  
</repository>  

Zaveďme závislosť:

<dependency>  
   <groupId>org.restlet.jse</groupId>  
   <artifactId>org.restlet</artifactId>  
   <version>2.2.2</version>  
</dependency>  

<dependency>  
   <groupId>org.restlet.jse</groupId>  
   <artifactId>org.restlet.ext.jackson</artifactId>  
   <version>2.2.2</version>  
</dependency> 

Projekt typu „Hello World“

Tu urobíme totálne jednoduchý projekt, ktorý vypíše Hello World.

Nový resource

Jedným z pilierov REST architektúry sú resources. Vyrobme si jednoduchý Resource, ktorý pri operácii GET vráti jednoduchý reťazec.

package sk.upjs.ics.novotnyr.restlet;

import org.restlet.resource.Get;
import org.restlet.resource.ServerResource;

public class HelloWorldResource extends ServerResource {
    @Get
    public String sayHello() {
        return "Hello World!";
    }
}

Všimnime si:

  • Resource v Restlete bežiaci na serveri je trieda, ktorá dedí od ServerResource.
  • anotáciou @Get na metóde povieme, že metóda obslúži verb GET.

To je všetko, poďme nakonfigurovať server!

Spustenie servera

Jednoduchá trieda s main()om vytvorí a spustí server:

package sk.upjs.ics.novotnyr.restlet;

import org.restlet.Server;
import org.restlet.data.Protocol;

public class SimpleRestletRunner {
    public static void main(String[] args) throws Exception {
        Server server = new Server(Protocol.HTTP, 8182, HelloWorldResource.class);
        server.start();
    }
}

Vyrobíme server počúvajúci na protokole HTTP, porte 8182 a akúkoľvek požiadavku pošle na čerstvo vyrobený, priam ešte z kompilátora teplý HelloWorldResource.

Spusťme a odskúšajme!

Spusťme SimpleRestletRunner a dostaneme logovaciu hlášku

17.11.2012 19:13:17 org.restlet.engine.http.connector.HttpServerHelper start
INFO: Starting the internal HTTP server on port 8182

Otvorme browser, navštívme http://localhost:8182/ a uvidíme

Hello World!

Tešíme sa, dosiahli sme totiž prvý míľnik tohto článku.

Deja vu?

Pripomína vám to servlety? Bodaj by nie, Restlet bol v mnohom inšpirovaný servletmi. Ak chcete, prečítajte si ságy o ceste k Restletu a o tom, aká bola motivácia za týmto projektom.

Jediný dôležitý rozdiel: kým servlety sú bezstavové a v systéme existuje typicky jediná inštancia servletu, ktorá obsluhuje požiadavky, v Restlete platí opačná filozofia:

Resources sú stavové objekty — s každou požiadavkou sa vytvorí nová inštancia objektu, ktorá sa po dobehnutí zahodí.

Zásada tiež znamená, že resource nemusí byť thread-safe, pretože nehrozí žiadny konflikt medzi vláknami pristupujúcimi k jednej inštancii.

Získavanie resource. HTTP GET a JSON serializácia

Predstavme si, že máme obchod s čokoládou, pre ktorý chceme zverejniť RESTované API fičiace nad JSONom.

Bude to veľmi jednoduché, pretože Restlet podporuje automatickú serializáciu objektov do rozličných formátov a JSON mu nerobí problém.

Doménové objekty

Základom bude objekt pre čokoládu. Budeme evidovať len názov a percento kakaa+kakaového masla a budeme to robiť v klasickom Java beane s gettermi a settermi.

package sk.upjs.ics.novotnyr.restlet;

public class Chocolate {
    private String title;

    private int percentage;

    public Chocolate() {
        // empty constructor
    }

    public Chocolate(String title, int percentage) {
        this.title = title;
        this.percentage = percentage;
    }

    public int getPercentage() {
        return percentage;
    }

    public String getTitle() {
        return title;
    }

    public void setPercentage(int percentage) {
        this.percentage = percentage;
    }

    public void setTitle(String title) {
        this.title = title;
    }
}

Knižnice pre serializáciu

Na to, aby sme vedeli veľmi jednoducho serializovať objekty do JSONu, dodáme do projektu niekoľko knižníc. Podpora je riešená cez knižnicu Jackson (nemýliť si so zosnulým popspevákom).

Verzia s ručným stiahnutím:

Všetky knižnice nájdeme niekde v adresári lib v inštalačnom adresári Restletu.

  1. org.restlet.ext.jackson.jar predstavuje extension, teda rozšírenie, teda plug-in, teda zásuvný modul pre Restlet s dodatočnou funkcionalitou — teda integráciou s Jacksonom.
  2. z adresára com.fasterxml.jackson_2.2 potrebujeme nasledovnú dávku knižníc

    1. com.fasterxml.jackson.annotations.jar
    2. com.fasterxml.jackson.core.jar
    3. com.fasterxml.jackson.csv.jar
    4. com.fasterxml.jackson.databind.jar
    5. com.fasterxml.jackson.smile.jar
    6. com.fasterxml.jackson.xml.jar
    7. com.fasterxml.jackson.yaml.jar

    Prakticky ide o všetky JARká z adresára, s výnimkou com.fasterxml.jackson.jaxb.jar. Ešte stálenechcete používať Maven?

Verzia s Mavenom

Pridajme závislosť:

<dependency>  
   <groupId>org.restlet.jse</groupId>  
   <artifactId>org.restlet.ext.jackson</artifactId>  
   <version>2.2.2</version>  
</dependency>  

Resource pre zoznam čokolád

Na rozdiel od predošlého resource už nebudeme vracať jednoduchý String, ale zoznam objektov. S každou požiadavkou vyrobíme nový zoznam ukážkových čokolád a vrátime ho z metódy.

public class ChocolatesResource extends ServerResource
{
    @Get("json")
    public List<Chocolate> listChocolates() {
        List<Chocolate> chocolates = new ArrayList<Chocolate>(2);
        chocolates.add(new Chocolate("Lindt Excellence 70%", 70));
        chocolates.add(new Chocolate("Milka Alpenmilch", 40));
        chocolates.add(new Chocolate("Christmas Angel Figure", 15));

        return chocolates;
    }
}

Aha, v Get sa objavila novinka: deklarácia pre media type na výstupe. Inak povedané, musíme povedať, že na výstupe bude json.

Spustenie servera

Otestujeme to jednoducho: upravme náš kód pre spustenie servera.

Server server = new Server(Protocol.HTTP, 8182, ChocolatesResource.class);

Opäť navštívme http://localhost:8182/ a sledujme výstup:

[{"percentage":70,"title":"Lindt Excellence 70%"},{"percentage":40,"title":"Milka Alpenmilch"},{"percentage":15,"title":"Christmas Angel Figure"}]

Čo sa všetko stalo??

Ako funguje táto mágia?

Ak príde do Resource požiadavka, zavolá sa príslušná metóda (listChocolates()). Restlet zoberie návratovú hodnotu a zistí, že ju treba zjsonifikovať (na základe anotácie @Get("json")). Zavolá preto mašinériu Jacksona, ktorý tento zoznam čokolád automaticky prehodí do JSON reprezentácie.

Tešíme sa, druhý míľnik je za nami!

Vytváranie resource. HTTP POST a JSON deserializácia

Resource nemusí podporovať len čítanie — to by bola dosť smutná situácia, keď zo systému viete čítať, ale nemáte možnosť doň narvať žiadne informácie. Vyrobme si teda metódu, ktorá prijme jednú čokoládu a… vypíše ju na serveri na konzolu. (V skutočnosti by sme ju pridávali do databázy, ale o to teraz nejde.)

Na vkladanie údajov môžeme použiť zaužívaný verb POST. Aký bude plán? Najprv dorobíme metódu, ktorá spracuje POSTovanie údajov. Vstupom bude čokoláda, výstupom „nič”:

@Post
public void addChocolate(Chocolate chocolate) {
    System.out.printf("Adding %s with %d %% of cocoa butter/mass\n", chocolate.getTitle(), chocolate.getPercentage());
}   

Všimnime si, že sme použili anotáciu @Post. V tomto prípade neuvádzame typ json, lebo ten sa vzťahuje na media type výstupu. My nemáme žiadny výstup a teda sa o túto vec nemusíme starať.

A to je všetko, poďme testovať.

Reštart servera a testovanie

Reštartnime server a narvime dáta do resource, napr. cez curl:

curl -v -X POST http://localhost:8182/ -H "Content-Type:application/json" -d " {\"title\" : \"Lindt Excellence 70%%\", \"percentage\": 70 } "  --trace-ascii -

Výsledkom bude utešený výpis na serveri.

Tretí míľnik je za nami!

Nemám curl, pomoc!

Ak curl nemáme (lebo sme biedni windowsáci), môžeme si ho stiahnuť (binárky existujú pre rozličné platformy; áno, aj pre Windows), alebo použijeme niektorý z plug-inov pre Firefox či Chrome, napr. HttpRequester.

Ak curl máme, nezľakneme sa príkazu. V skutočnosti chceme poslať na server toto:

{"title" : "Lindt Excellence 70%", "percentage": 70 }

Kvôli technickým obmedzeniam musíme escapovať úvodzovky (cez \") aj percentá (ktoré sú v URL kódovaní špeciálny znak a teda píšeme %%).

Čo sa stalo?

Hlavička Content-Type je veľmi dôležitá. Podľa nej sa totiž server rozhoduje, ktorý deserializačný mechanizmus má naštartovať. Ak máme application/json, vytancuje na pódium Jackson a postará sa o dejsonifikáciu vstupu a jeho mapovanie na objekt typu Chocolate.

Ak hlavičku zabudnete zaslať, nestane sa na serveri nič; ba čo je horšie, server vašu požiadavku potichu odignoruje.

Sumár

Všimnime si, ako jednoducho možno nakonfigurovať jednoduchý resource v Restlete. Stačí oddediť od triedy, porobiť zopár anotácií, dodať JAR tu a JAR tam a všetko funguje. Ak by tento článok mal pokračovanie, rozhodne by bolo dobré zistiť, ako postaviť nielen serverovskú, ale aj klientskú časť pre RESTované API. Restlet podporuje aj takúto možnosť. Tento príklad zároveň nepodporuje mapovanie URI adries: inak povedané, náš server vie obslúžiť len jeden Resource. Možno nabudúce!

Výsledný projekt

Stiahnite si výsledný projekt restlet-chocolate pre Eclipse. Alternatívne si môžete stiahnuť mavenovskú verziu projektu restlet-chocolate z Githubu.

Pridaj komentár

Vaša e-mailová adresa nebude zverejnená. Vyžadované polia sú označené *