Przeglądając ostatnio dokumentację API serwisu Blip natrafiłem na stwierdzenie, iż architektura intefejsu programistycznego Blip.pl wykonana jest zgodnie ze wskazaniami stylu REST. Generalnie wiedziałem o co chodzi; czytelnie skonstruowane odnośniki reprezentują zasoby serwisu a do manipulowania nimi stosuje się metody protokołu HTTP (GET – pobranie zasobu, POST – utworzenie itp.)

Również niedawno Mateusz wrócił z JDD i Cooluarów, które odbywały się w Krakowie. Zdał mi relację z ciekawej prezentacji dotyczącej RESTful Web Services. Wydały mi się ciekawą i “lekką” alternatywą dla usług sieciowych opartych o protokoły SOAP i WSDL. W tym wpisie zamieszczam moje rozpoznanie tematu.


REST

Zacznijmy od nazwy. Termin REST określa styl architektury typu klient-serwer, w której komunikacja polega na przesyłaniu reprezentacji zasobów – wolne tłumaczenie definicji z Wikipedii. Zasobem może być każdy sensowny „obiekt” (lub „byt”), do którego można się jakoś odnieść a reprezentacja to dokument przedstawiający faktyczny lub pożądany stan tego obiektu.

Przykład

Zasobem może być np. model samochodu a pod adresem http://samochody.info/peugeot/106 znajdować się może dokument w formacie HTML, który opisuje go w jakiś sposób. Dokument ten może zawierać odnośnik http://samochody.info/peugeot/106/zdjecie.jpg, który jest reprezentacją zdjęcia tego modelu samochodu. Z kolei odwiedzając http://samochody.info/peugeot otrzymamy listę wszystkich modeli wraz z odnośnikami.

Styl REST został początkowo opisany w kontekście protokołu HTTP; istnieje w nim wiele sposobów reprezentacji zasobów (URL, URI, typy MIME) oraz metod manipulacji nimi (GET, POST, PUT, DELETE).


RESTful Web Services

RESTful Web Services są usługami sieciowymi opartymi o protokół HTTP i zasady stylu REST. Zazwyczaj metody protokołu odpowiadają typowym operacjom CRUD:

  • POST – CREATE
  • GET – READ
  • PUT – UPDATE
  • DELETE – DELETE


JAX-RS 1.0 (JSR-311)

JSR-311 to formalny dokument opisujący API do tworzenia usług sieciowych w stylu REST z użyciem języka i platformy Java (Java API for RESTful Web Services – JAX-RS). Wersja 1.0 specyfikacji została wydana 8 września 2008 roku. To nowoczesne API, korzysta ze wszelkich dobrodziejstw Javy – adnotacji, wstrzykiwania zależności.

Usługa sieciowa stworzona z użyciem JAX-RS składa się (głównie) z dwóch rodzajów klas:

  • klas zasobów,
  • klas dostawców.

Klasy zasobów implementują zasoby i operacje z nimi związane, są to zwykłe POJO a wszelkie sprawy związane z protokołem HTTP załatwiamy adnotacjami:

Adnotacja Opis
@GET, @POST itd. Określa, które metody klasy odpowiadają poszczególnym metodom HTTP.
@Path Pozwala na zdefiniowanie szablonu URI używanego przez usługę sieciową.
@Produces, @Consumes Przypisuje typ MIME reprezentacji, które jest tworzona lub przetwarzana przez metodę klasy.
@QueryParam, @PathParam, @CookieParam, @HeaderParam Określa, skąd brane są wartości parametrów przekazywanych do metod klasy.

Klasy dostawców zgodnie z nazwą dostarczają różne usługi. Podzieleni są na trzy grupy:

Grupa Opis
Dostawcy encji Zajmuję się mapowaniem obiektów Javy na reprezentację oraz na odwrót. Implementują interfejsy MessageBodyReader i/lub MessageBodyWriter.
Dostawcy kontekstu Dostarczają kontekst klasom zasobów i innym dostawcom. Implementują interfejs ContextResolver<T>, gdzie T jest typem kontekstu.
Dostawcy mapowania wyjątków Mapują wyjątki rzucane przez inne klasy na odpowiedź HTTP. Implementują interfejs ExceptionMapper<T>

Przykład

@Path("/")
public class CarResource {
 
    @GET
    @Path("/{make}/{model}/")
    @Produces("application/xml")
    public CarModel getModel(@PathParam("make") String make, @PathParam("model") String model) {
        // ...
    }
 
    @POST
    @Path("/")
    @Consumes("application/json")
    public Response addModel(CarModel model) {
        // ...
    }
}

Powyższy kod przedstawia prostą klasę służącą do implementacji zasobu reprezentującego modele samochodów. Zasób ten jest dostępny na głównej ścieżce URI (@Path("/"), np. http://samochody.info/).

Użycie metody GET zwróci nam opis modelu samochodu w formacie XML (@GET, @Produces("application/xml")). URI, który należy podać klientowi to np. http://samochody.info/peugeot/106/ (@Path("/{model}/{make}/")). Metoda getModel przyjmuje dwa parametry typu String, które są pobierane z URI (@PathParam("model") i @PathParam("make") – odpowiadają one fragmentom {model} i {make} z adnotacji @Path). Druga metoda tworzy nowy zasób. Dane są przekazywane metodą POST w formacie JSON.


Przykłady z użyciem Apache CXF

CXF jest frameworkiem służącym do tworzenia wszelkiego rodzaju usług. Wspiera wiele różnych protokołów oraz standardów: SOAP, WSDL, WS-Security, JAX-WS oraz właśnie JAX-RS. Usługi stworzone z jego pomocą możemy używać w różnych kontenerach; od Tomcata i Springa po serwery aplikacyjne zgodne z całą specyfikacją Java EE. CXF zresztą wchodzi w skład serwera Apache Geronimo.

Poniższe przykłady są do ściągnięcia w postaci spakowanych projektów mavenowych. Kompilemy je wydając polecenie mvn compile a uruchamiamy poprzez mvn exec:java.

Car Service

Pierwszy przykład to prosta usługa do przechowywania informacji o modelach samochodów (która była wspominana wcześniej). Przy uruchomieniu zawiera tylko model Peugeota 106, dostępny pod adresem http://localhost:9000/peugeot/106/ (wystarczy skorzystać z przeglądarki).

Żeby dodać jakiś model samochodu, musimy wysłać do usługi jego reprezentację w formacie JSON metodą POST protokołu HTTP. Możemy to zrobić przy użyciu programu curl:

curl -v -H "Content-Type: application/json" -d {"car-model":{"make":"Renault","model":"Clio"}} \
http://localhost:9000/

Todo Service

Tak jak pierwszym programem napisanym w trakcie nauki języka programowanie jest Hello World (albo Hello, I am JanB), tak dla frameworków webowych zaczyna przyjmować się zwyczaj, że pierwszym programem jest lista zadań (śmiem twierdzić, że mój kolega jest autorem tego zwyczaju). Zgodnie z tą tradycją zachęcam do testowania usługi sieciowej RESTful – Todo Service.

Wszystkie zadania:

curl http://localhost:9000/todo/

Nowe zadanie:

curl -v -H "Content-Type: application/xml" -d "<task><description>Nowe zadanie</description></task>" \
http://localhost:9000/todo/

Uaktualnienie zadania:

curl -v -H "Content-Type: application/xml" \
-X PUT -d "<task><id>2</id><description>Nowe zadanie</description><done>true</done></task>" \
http://localhost:9000/todo/

Usunięcie zadania:

curl -v -X DELETE http://localhost:9000/todo/1/


Linki