Il termine REST è un acronimo per REpresentational State Transfer ed indica una architettura web standard che utilizza il protocollo HTTP per la comunicazione dei dati. Esso è focalizzato sul concetto di risorsa, dove ad ogni risorsa si accede attraverso una interfaccia comune utilizzando metodi standard del protocollo HTTP (es. GET, PUT, POST, DELETE, etc.). Ogni risorsa è identificata da un URI ed i dati possono essere rappresentati in diversi formati quali XML e JSON.
Differentemente da SOAP la struttura dei messaggi scambiati tra client e server non è standardizzata, quindi il client deve conoscere a priori la specifica delle api esposte dal server. Sebbene questo possa essere considerato uno svantaggio per il programmatore, l’assenza di una tale specifica consente di progettare servizi più snelli e prestazionalmente migliori. In SOAP, ad esempio, molto tempo di elaborazione è dedicato alla gestione della busta SOAP (imbustamento ed apertura).
In linea di principio nella progettazione di un Web Service REST la sola linea guida che è importante seguire è che deve essere semplice da utilizzare per il client e da facilmente manutenibile.
Creare un Progetto Standalone
JAX-RS è parte della specifica Java EE6 ed è pensata per semplificare lo sviluppo di applicazioni RESTful. Essa consiste in un insieme di interfacce ed annotazioni definite ne package javax.ws.rs.
In questo tutorial è utilizzato i framework Jersey che è l’implementazione di riferimento per tale specifica (JSR 311 e JSR 339). Ovviamente esistono altre implementazioni altrettanto valide come ad esempio RESTEasy.
Il modo più semplice per la realizzazione di un progetto con Jersey è l’utilizzo di Maven. Utilizziamo quindi il seguente comando maven per generare il progetto base con Jersey e Grizzly come container:
1 2 3 4 5 6 7 8 |
mvn archetype:generate -DarchetypeArtifactId=jersey-quickstart-grizzly2 -DarchetypeGroupId=org.glassfish.jersey.archetypes -DinteractiveMode=false -DgroupId=it.javaboss -DartifactId=restful-ws -Dpackage=it.javaboss.restfulws -DarchetypeVersion=2.23.2 |
Il progetto così generato nella cartella restful-ws
può ora essere importato nell’IDE di sviluppo (Eclipse ad esempio). Analizzando il progetto si nota che maven ha generato due classi:
Main.java
contenente il codice necessario per eseguire il bootstrap del container Grizzly e la configurazione e deploy del servizio di esempio;MyResource.java
che contiene una semplice implementazione di servizio REST (convenzionalmente detta risorsa JAX-RS).
Eseguiamo il main
, quindi apriamo il browser andiamo all’url http://localhost:8080/myapp/myresource. Nella pagina comparirà la string:
1 |
Got it! |
generata dal metodo getIt()
della classe MyResource.java
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
/** * Root resource (exposed at "myresource" path) */ @Path("myresource") public class MyResource { /** * Method handling HTTP GET requests. The returned object will be sent * to the client as "text/plain" media type. * * @return String that will be returned as a text/plain response. */ @GET @Produces(MediaType.TEXT_PLAIN) public String getIt() { return "Got it!"; } } |
Proviamo ora ad inserire nel browser l’url http://localhost:8080/myapp/application.wadl. La pagina visualizzata è un XML che fornisce una descrizione machine-readable delle risorse esposte dal container secondo la specifica WADL (Web Application Description Language).
Una risorsa JAX-RS non è altro che una classe POJO annotato opportunamente e che fornisce i cosiddetti metodi di risorsa (resource method) che sono in grado di gestire le richieste HTTP verso URI a cui la risorsa è legata. Nel nostro caso, la risorsa espone un singolo metodo getIt()
che è in grado di gestire richieste di tipo GET destinate all’URI myapp/myresource e
produce risposte con il contenuto del messaggio di tipo plain-text
(nel caso specifico restituisce “Got it!” per tutte le richieste).
HelloWorld
Come di consueto creiamo il nostro esempio base implementando un servizio più complesso del semplice esempio visto precedentemente, che riceve un parametro in input e produce un output variabile.
1 2 3 4 5 6 7 8 9 10 |
@Path("/hello") public class HelloWorldService { @GET @Path("/{name}") @Produces(MediaType.TEXT_PLAIN) public Response getMsg(@PathParam("name") String name) { String output = "Hello " + name; return Response.status(200).entity(output).build(); } } |
Vediamo nel dettaglio come viene definito il servizio. Innanzitutto l’annotazione @Path
serve a legare (binding) la specifica risorsa ad un URI. In particolare possiamo trovare l’annotazione in due punti. Inserita a livello di classe indica una porzione di URI comune a tutti i metodo, mentre se presente a livello di metodo indica un URI specifica per quel metodo.
E’ anche possibile inserire negli URI dei parametri (definiti tra parentesi graffe) che possono poi essere catturati attraverso l’annotazione @PathParam
associata ad un parametro formale del metodo. Nel caso specifico l’URI /hello/{name}
è associato al metodo getMsg()
e qualsiasi stringa inserita al posto di {name}
è considerata una variabile con cui valorizzare il parametro name
del metodo.
Una interessate feature di JAX-RS è che è anche possibile specificare gli URI attraverso l’uso di espressioni regolari nella forma { "variable-name [ ":" regular-expression ] "}
. Ad esempio nel nostro caso avremmo potuto utilizzare:
1 2 3 4 |
@Path({name : [a-zA-Z]+}) public Response getMsg(@PathParam("name") String name) { .... } |
indicando in tal modo che l’URI per essere legata al metodo getMsg()
deve essere composta da una sequenza di caratteri alfabetici maiuscoli o minuscoli, ovvero numeri e caratteri speciali non sono ammessi. Questo implica che nel caso in cui fosse presente anche un solo carattere non alfabetico il processo di binding non avrebbe successo.
L’annotazione @GET
indica al container che la risorsa è accessibile attraverso il metodo HTTP GET. Quindi è possibile eseguire il test semplicemente inserendo nel browser l’url http://localhost:8080/myapp/hello/JavaBoss. Se avessimo utilizzato un altro metodo HTTP, ad esempio POST o PUT, avremmo avuto bisogno di un client REST per effettuare il test, come ad esempio l’estensione chrome Advanced Rest API.
Creare una Web Application
Nell’esempio precedente abbiamo creato una applicazione in grado funzionare in modo standalone senza la necessità di un container J2EE che la ospiti, grazie all’utilizzo di Grizzly incluso nel progetto. Vediamo ora come realizzare la stessa applicazione ma questa volta al fine di essere ospitata, ad esempio, su Tomcat 7.
Utilizzando ancora Maven eseguiamo il seguente comando:
1 2 3 4 5 6 7 8 |
mvn archetype:generate -DarchetypeArtifactId=jersey-quickstart-webapp -DarchetypeGroupId=org.glassfish.jersey.archetypes -DinteractiveMode=false -DgroupId=it.javaboss -DartifactId=restful-webapp -Dpackage=it.javaboss.restfulws -DarchetypeVersion=2.23.2 |
Come fatto precedentemente importiamo il progetto restful-webapp
in Eclipse e eseguiamo il deploy su Tomcat engine che avremo precedentemente configurato nell’IDE. Una volta avviato Tomcat inserite nel browser l’url http://localhost:8080/restful-webapp/ ed apparirà la pagina index.jsp
di default dell’applicativo, dove è presente l’url all’unico resource method definito http://localhost:8080/restful-webapp/webapi/myresource.
Trattandosi di una applicazione web ci aspettiamo che nel descrittore web.xml
sia definita la servlet dedicata alla gestione delle richieste verso le risorse REST, che nel caso di jersey è org.glassfish.jersey.servlet.ServletContainer
, mappata sull’url /webapi/*
.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<servlet> <servlet-name>Jersey Web Application</servlet-name> <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class> <init-param> <param-name>jersey.config.server.provider.packages</param-name> <param-value>it.javaboss.restfulws</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>Jersey Web Application</servlet-name> <url-pattern>/webapi/*</url-pattern> </servlet-mapping> |
Creazione di un Client
Jersey fornisce anche i metodi necessari per l’implementazione di un client per i servizi REST. La classe seguente invoca il servizio che abbiamo creato nei due esempi precedenti con poche righe di codice.
1 2 3 4 5 6 7 8 9 10 11 |
public class RestClient { public static void main(String[] args) { Client client = ClientBuilder.newClient(); WebTarget target = client.target("http://localhost:8080/restful-webapp/webapi"); Response response = target.path("myresource").request().get(); System.out.println(response.getStatus()); System.out.println(response.readEntity(String.class)); } } |
Download Codice Sorgente
I due progetti completi sono disponibile qui: restful-ws e restful-webapp.