Come la Action di Struts o il Managed Bean di JSF, il Controller è il componente dell’architettura di Spring MVC che si occupa di gestire le request utente. I controller di Spring sono delle semplici classi java (POJO) che non necessitano di implementare alcuna interfaccia, come avveniva ad esempio in Struts 1.x, ma il framework adotta una strategia basata su annotazioni. Questo lascia al programmatore una enorme flessibilità nella definizione di un controller ed in particolare nella firma dei metodi che lo costituiscono.
Questo articolo vuole essere un breve vademecum delle principali possibilità offerte dal framework nella dichiarazione di un controller.
Definizione di un Controller
Le annotazioni principali per la definizione di un controller sono @Conttoller
e @RequestMapping
. La Dispatcher Servlet, opportunamente configurata, ricerca le classi annotate con @Controller
e ne registra i metodi. Il mapping tra metodi del controller ed URL di invocazione è ottenuto attraverso l’annotazione @RequestMapping
. Per un esempio di utilizzo si veda il LoginController.java
presentato nell’articolo Primi Passi con Spring MVC (parte 2). I parametri accettati dall’annotazione @RequestMapping
sono diversi:
path , value |
Utilizzato per specificare la URI o le URI servite dal controller. Sono possibili diversi formati:
Può essere utilizzato sia a livello di classe che di metodo. Il path definito a livello di classe è ereditato dai metodo e contribuisce a definire l’URI utilizzandolo com prefisso. |
method |
Metodo o array di metodi HTTP serviti dal controller. Valori ammessi sono GET, POST, HEAD, OPTIONS, PUT, PATCH, DELETE, TRACE, tutti definiti nella classe RequestMethod . |
headers |
Header ammessi nella richiesta HTTP servita dal metodo. Possono essere definiti attraverso una sequenza del tipo “Header1=Value1, …, HeaderN=ValueN”, eventualmente anche utilizzando espressioni negate “Header!=Value”. |
consumes |
Consente di limitare la gestione alle sole request con una o più media type specifici, es. {“text/plain”, “application/*”}. L’espressione può essere negata utilizzando l’operatore “!”, es “!text/plain”. |
Response Body
Principalmente utilizzata ai fini di test, ad esempio per verificare la raggiungibilità dei servizi, l’annotazione @ResponseBody indica che il valore restituito dal metodo del controller costituisce il body della response. Nessuna altra elaborazione è necessaria.
1 2 3 4 5 6 |
@RequestMapping(value = "/ping", method = RequestMethod.GET) @ResponseBody public String sayHallo() { SimpleDateFormat formatter = new SimpleDateFormat("dd/MM/yyyy hh:MM:ss"); return formatter.format( Calendar.getInstance().getTime() ); } |
Fallback Method
Il fallback method è un metodo di un controller che viene utilizzato dal framework ogni volta che non esiste un metodo che gestisca la URI inserita.
1 2 3 4 5 |
@RequestMapping("*") @ResponseBody public String fallbackMethod(){ return "URI non gestito"; } |
Attributi di Sessione
Per la memorizzazione di parametri nella sessione HTTP in modo trasparente il framework mette a disposizione l’annotazione @SessionAttributes
. E’ associata alla classe che implementa il controller ed elenca gli attributi che si intende inserire in sessione rendendoli quindi disponibili a request successive. Nell’esempio LoginController
il nome dell’utente che esegue il login è un attributo che tipicamente è inserito in sessione.
1 2 3 4 5 6 7 8 9 10 11 |
@Controller @SessionAttributes("name") public class LoginController { @RequestMapping(value = "/login", method = RequestMethod.POST) public String handleUserLogin(ModelMap model, @RequestParam String name, @RequestParam String password) { if ( service.validateUser( name, password) ) { model.put("name", name); ... } ... } |
Nel momento in cui il parametro name
è inserito nell’oggetto ModelMap
(operazione put()
) viene automaticamente promosso a parametro di sessione ed inserito nella sessione HTTP. Il parametri rimangono in sessione fino a quando un altro controller non utilizza l’oggetto SessionStatus per chiudere la sessione:
1 2 3 4 5 6 |
@RequestMapping(value = "/logout") @ResponseBody public String handleUserLogout( SessionStatus status ) { status.setComplete(); return "Logged out!"; } |
Parametri di Input del Controller
Il framework non prevede vincoli specifici nella firma dei metodi del controller lasciando il programmatore libero di scegliere tra una varietà di argomenti e valori restituiti.
Path Variable
Nel caso di URI con placeholder (vedi sopra) è possibile recuperare i valori assunti dal segnaposto utilizzando l’annotazione @PathVariable
. Estendiamo, ad esempio, il progetto dell’articolo Primi Passi con Spring MVC (parte 2) introducendo la lista dei todo assegnati ai vari utenti. Il dettaglio di uno specifico todo identificato da un id
è ottenibile attraverso il seguente controller:
1 2 3 4 5 6 |
@RequestMapping( value="/{id}", method = RequestMethod.GET ) public String showTodo( ModelMap model, @PathVariable("id") Integer id ) { Todo todo = service.getTodo(id); model.addAttribute( "todo", todo ); return "todo"; } |
l’id
nell’url è collegato al parametro id
del metodo (il nome del parametro non deve necessariamente essere uguale) che poi lo utilizza per recuperare il todo e inserirlo nel model.
Parametri della Request
Per il recupero dei parametri nella request il framework introduce l’annotazione @RequestParam
. Un esempio è il metodo handleUserLogin()
che recupera nome utente e password inseriti nella form di login dall’utente.
I parametri in request possono essere inserite ad esempio utilizzando un form che viene sottomesso con metodo POST o GET, oppure più semplicemente inserendoli nell’URL come query string.
Spring provvede automaticamente ad eseguire la conversione del parametro nel tipo definito nella firma del metodo. In alternativa, se il parametro del metodo è di tipo Map<String,String>
o MultiValueMap<String,String>
, tutti i parametri della request vengono inseriti nella mappa.
Di default il parametro è obbligatorio, ma è possibile renderlo opzionale attraverso l’attributo required
dell’annotazione@RequestParam
. I’attributo default
consente invece, nel caso di parametro opzionale, di definire un valore di default.
1 2 3 4 5 6 7 8 9 |
@RequestMapping("/params") @ResponseBody public String getParams( @RequestParam MultiValueMap<String, String> params){ StringBuilder builder = new StringBuilder(); for ( String key : params.keySet() ) { builder.append( key + "=" + params.get(key) + "<br/>" ); } return builder.toString(); } |
Parametri nell’Header
Analogamente ai request parameter è possibile recuperare gli header parameter utilizzando l’annotazione @RequestHeader
. Anche in questo caso è possibile ottenere il singolo parametro o la mappa di tutti i parametri presenti nell’header.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
@RequestMapping("/header") @ResponseBody public String getHeader( @RequestHeader("Host") String host, @RequestHeader("Accept") String accept) { return "Host = " + host + "<br/> Accept = " + accept; } @RequestMapping("/headers") @ResponseBody public String getHeaders(@RequestHeader MultiValueMap<String, String> headers) { StringBuilder builder = new StringBuilder(); for ( String key : headers.keySet() ) { builder.append( key + "=" + headers.get(key) + "<br/>" ); } return builder.toString(); } |
Accesso ai Cookie
Attraverso l’annotazione @CookieValue
associata ad un argomento del metodo del controller è possibile consente ai cookie HTTP dell’utente. Nell’esempio seguente è recuperato il cookie JSESSIONID
utilizzato dalle applicazioni web J2EE per il mantenimento della sessione utente.
1 2 3 4 5 |
@RequestMapping("/cookie") @ResponseBody public String handle(@CookieValue("JSESSIONID") String cookie) { return cookie; } |
La conversione di tipo del cookie è eseguita automaticamente dal framework nel caso in cui il parametro del metodo non sia di tipo String.
Request Body
L’uso della direttiva <mvc:annotation-driven/>
nel file di configurazione di Spring abilita il supporto alla serializzazione degli oggetti java in formato XML e viceversa, utilizzando il framework JAXB. Questo consente ad un client di inviare un oggetto serializzato nel body della request ed al controller di recuperare direttamente l’oggetto deserializzato semplicemente utilizzando l’annotazione @RequestBody
.
Nell’esempio seguente un oggetto Todo
è passato in input al metodo del controller prelevandolo dalla request body e deserializzandolo con JAXB.
1 2 3 4 5 6 |
@RequestMapping( value="/add", method = RequestMethod.POST ) @ResponseBody public String addTodo( @RequestBody Todo todo) { service.addTodo(todo); return todo.toString(); } |
Naturalmente la classe le proprietà ed i metodo dell’oggetto Todo
dovranno essere opportunamente annotato con le annotazioni di JAXB secondo necessità. Sicuramente dovrà essere presente l’annotazione @XmlRootElement
a livello di classe.
1 2 3 4 5 6 7 8 9 10 |
@XmlRootElement public class Todo { private Integer id; private Date date; private String user; private String description; private boolean done = false; // getter and setter } |
Multipart Form Data
Il framework consente anche di gestire richieste con content-type
uguale a multipart/form-data
, generalmente utilizzata per allegare interi file alla request (upload). Per farlo è necessario abilitare un bean MultipartResolver
, dichiarandolo nel file di configurazione di Spring, che viene utilizzato dalla DispatcherServelt
per il parsing della request.
Ad esempio, l’annotazione @RequestPart
associata da un argomento di tipo MultipartFile
consente di eseguire l’upload di un file e di gestirlo nel controller prelevandolo dalla request.
1 2 3 4 5 6 7 8 9 |
@RequestMapping(value = "/upload", method = RequestMethod.POST) public String handleFormUpload(@RequestPart("file") MultipartFile file) throws IOException { if (!file.isEmpty()) { byte[] bytes = file.getBytes(); // store the bytes somewhere return "..."; } return "..."; } |
Altro
Esiste una varietà di altre possibilità per gli argomenti dei metodi di un controller che non discuteremo nell’articolo. Per maggiori dettagli si rimanda alla documentazione ufficiale al paragrafo Method Arguments.
Parametri di Output del Controller
Si rimanda alla seconda parte di questo articolo: Spring MVC Controller (parte 2).
Codice Sorgente
Il codice sorgente completo degli esempi presentati è scaricabile qui login-sprmvc.