Nell’articolo Primi Passi con JAX-RS abbiamo mostrato come creare una applicazione JAX-RS di base utilizzabile sia in modalità standalone che come web application. Inoltre è stato mostrato un primo semplice esempio di servizio REST ed un client per invocarlo. Nel presente articolo approfondiamo i diversi aspetti del framework.
Per testare il codice riportato nell’articolo si consiglia l’utilizzo dell’estensione chrome Advanced Rest Client.
URI Matching
Il binding di una URI con la specifica risorsa avviene attraverso l’utilizzo dell’annotazione @Path
. Ogni classe POJO annotata con @Path
è automaticamente riconosciuta come root resource class. I metodi della classe per essere riconosciuti come servizi REST devono a loro volta essere annotati con una delle annotazioni identificative dei metodi HTTP, e opzionalmente essere annotati con @Path
e quindi diventare delle sotto-risorse.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
@Path("method") public class HttpMethodService { @GET public Response getMethod() { return Response.status(200).entity( "GET METHOD" ).build(); } @PUT public Response putMethod() { return Response.status(200).entity( "PUT METHOD" ).build(); } @POST public Response postMethod() { return Response.status(200).entity( "POST METHOD" ).build(); } @DELETE public Response deleteMethod() { return Response.status(200).entity( "DELETE METHOD" ).build(); } @HEAD public Response headMethod() { return Response.status(200).entity( "HEAD METHOD" ).build(); } @OPTIONS public Response optionsMethod() { return Response.status(200).entity( "OPTIONS METHOD" ).build(); } } |
Per default con Jersey il life-cycle di una root resource è per-request, ovvero la classe associata è istanziata ogni volta che l’url richiesto corrisponde a quello risorsa. Questo se da un lato semplifica la programmazione non dovendo gestire situazioni di accesso concorrente, dall’altro sono la principale causa di carenza nelle performace. Per modificare il comportamento di default è possibile utilizzare una delle seguenti annotazioni:
@RequestScoped | Comportamento di default applicato anche in assenza di annotazioni. La risorsa è istanziata ad ogni nuova richiesta e se utilizzata più volte nella stessa richiesta non viene istanziata di nuovo. |
@PerLookup | In questo caso la risorsa è generata di nuovo ad ogni utilizzo anche se all’interno della stessa request. |
@Singleton | La risorsa è unica per applicazione. |
Iniezione dei Parametri
Nel post Post to Post Links II error: No post found with slug "primi-passi-con-jax-rs-parte-1" abbiamo visto un esempio molto semplice di passaggio di parametri al servizio REST attraverso l’estrazione porzioni di stringa dall’URI legato alla risorsa. Riepiloghiamo in questa sezione i vari modi in cui JAX-RS consente il passaggio dei parametri dal client al server.
@PathParam
Abbiamo già visto come l’annotazione consente di iniettare il valore estratto dall’URI in un parametro di un risorse method. La stessa annotazione è però utilizzabile anche per:
- iniettare il valore in una variabile di istanza della classe:
1 2 3 4 5 6 7 8 9 10 |
@Path("parameter/{name}") public class ParameterService { @PathParam("name") private String name; @GET public Response getMethod() { return Response.status(200).entity( "NAME = " + name ).build(); } } |
- iniettare il valore come parametro del costruttore della classe:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
@Path("parameter/{name}") public class ParameterService { private String name; public ParameterService( @PathParam("name") String name ) { this.name = name; } @GET public Response getMethod() { return Response.status(200).entity( "NAME = " + name ).build(); } } |
Tali opportunità non sono ovviamente utilizzabili nel caso in cui la root resource class sia definita come singleton.
Ovviamente la stessa tecnica è utilizzabile per l’iniezione di parametri multipli, come nel seguente esempio.
1 2 3 4 5 6 7 8 9 10 11 |
@Path("parameter") public class MultyParameterService { @GET @Path("{day}/{month}/{year}") public Response dateMethod( @PathParam("day") String day, @PathParam("month") String month, @PathParam("year") String year) { return Response.status(200).entity( "Date = " + day + "/" + month + "/" + year ).build(); } } |
@QueryParam
Attraverso tale annotazione è possibile iniettare i valori estratti dalla parte di URI che corrisponde alle query string. Ad esempio la string JavaBoss nell’url https://www.google.it?q=JavaBoss. Come nel caso precedente, anche questa annotazione è utilizzabile sia a livello di metodo che a livello di variabile di istanza della classe o di un suo costruttore.
L’esempio seguente è attivato eseguendo la GET sull’urlo http://localhost:8080/myapp/parameter?from=1&to=10.
1 2 3 4 5 6 |
@GET public Response queryMethod( @QueryParam("from") int from, @QueryParam("to") int to) { return Response.status(200).entity( "Filter from " + from + " to " + to ).build(); } |
Nel caso in cui i parametri nella query string siano opzionali è possibile utilizzare l’annotazione @DefaulValue
nel modo seguente:
1 2 3 4 5 6 |
@GET public Response queryMethod( @DefaultValue("0") @QueryParam("from") int from, @QueryParam("to") int to) { return Response.status(200).entity( "Filter from " + from + " to " + to ).build(); } |
@Context
Tale annotazione è utilizzabile per ottenere informazioni di contesto relative ad una request o response corrente. In particolare è possibile iniettare la classe UriInfo
che consente la manipolazione dell’URI associata alla request e quindi l’estrazione delle query string come nell’esempio seguente:
1 2 3 4 5 6 |
@PUT public Response uriInfoMethod( @Context UriInfo info ) { String from = info.getQueryParameters().getFirst( "from" ); String to = info.getQueryParameters().getFirst( "to" ); return Response.status(200).entity( "Filter from " + from + " to " + to ).build(); } |
@MatrixParam
Oltre all’utilizzo delle query string, un altro modo per inviare parametri attraverso una URI sono i matrix parameter. SI tratta sostanzialmente di porzioni di URI del tipo “;name1=value1;name2=value2″ presente in una qualsiasi posizione. Un esempio di URI con matrix parameter è: http://example.com/apples;order=random;color=blue/2006/archive?year=2016.
Il seguente resource method estrae i parametri autore e country da un URI del tipo http://localhost:8080/myapp/parameter;author=JavaBoss;country=Italy.
1 2 3 4 5 6 |
@DELETE public Response matrixParamMethod( @MatrixParam("author") String author, @MatrixParam("country") String country) { return Response.status(200).entity( "Info -> author " + author + " country " + country ).build(); } |
@FormParam
JAX-RS consente di iniettare parametri in un resource method estraendoli da un form attraverso l’utilizzo di tale annotazione. Ad esempio il metodo:
1 2 3 4 5 6 7 8 |
@POST public Response login( @FormParam("username") String username, @FormParam("password") String password) { return Response.status(200) .entity("Autenticating user " + username + " with password " + password ).build(); } |
1 2 3 4 5 6 7 8 9 |
<html> <body> <form action="/parameter" method="post"> <p>username: <input type="text" name="username" /></p> <p>Password: <input type="text" name="username" /></p> <input type="submit" value="Login" /> </form> </body> </html> |
Manipolazione dell’Header
JAX-RS consente di accedere agli header parameter della request attraverso due possibili modalità. La prima prevede l’utilizzo dell’annotazione @HeaderParam
e la specifica del parametro richiesto. Un elenco degli header param standard è presente in questa pagina wiki. Ovviamente l’utente può utilizzare il proprio header parameter custom.
1 2 3 4 5 6 7 |
@Path("header") public class HeaderService { @GET public Response userAgent( @HeaderParam("user-agent") String userAgent ) { return Response.status(200).entity("User Agent: " + userAgent).build(); } } |
@Contex
t per recuperare l’oggetto HttpHeaders
attraverso il quale recuperare i parametri richiesti.
1 2 3 4 |
@PUT public Response userAgent( @Context HttpHeaders headers ) { return Response.status(200).entity("User Agent: " + headers.getHeaderString("user-agent")).build(); } |
Download Codice Sorgente
Il progetto completo degli esempi riportati nell’articolo è disponibile qui restful-ws.