Concludiamo la serie di articoli sulle basi di Spring MVC ed in particolare sulla implementazione di un controller, iniziata in Spring MVC Controller (parte 1), parlando della tipo di dato che può essere restituito dal controller e su come è gestita la navigazione. Questo perchè nella implementazione di un controller le informazioni che debbono essere restituite sono:
- il modello ovvero i dati mostrati dalla vista;
- la vista che deve renderizzare il modello.
Restituzione Diretta del Contenuto
Principalmente utilizzato nella implementazione di servizi REST (ovvero nei @RestController
) ma anche per implementare metodi di debug, l’annotazione @ResponseBody
consente di restituire direttamente al client il messaggio da visualizzare. Se il metodo del controller non restituisce un tipo String
interviene uno dei converter (bean che estendono l’interfaccia HttpMessageConverter
) resi disponibili dal framework.
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() ); } |
Naturalmente in questo caso non è restituita alcuna vista in quanto il messaggio inserito nel body della response e renderizzato direttamente dal browser.
1 2 3 4 |
<html> <head></head> <body>05/03/2018 04:03:10</body> </html> |
Restituzione della Vista
Il controller può restituire la vista da renderizzare in diversi modi. Il primo è restituendo il nome della vista che sarà poi utilizzato dal ViewResolver
per associarla, ad esempio, alla pagina jsp. In questo caso il metodo del controller può accedere ed aggiornare il model dichiarandolo come argomento del metodo oppure utilizzando l’annotazione @ModelAttribute
.
1 2 3 4 5 |
@RequestMapping( method = RequestMethod.GET ) public String showTodos( Model model) { model.addAttribute( "todos", service.getTodos() ); return "todos"; } |
Nell’esempio il metodo showTodos del controller riceve il model come argomento e lo aggiorna con la lista dei todo. Quindi restituisce il nome della vista todos
che sarà poi convertita dal ViewResolver
in /WEB-INF/views/todos.jsp
. Alternativamente è possibile utilizzare un oggetto ModelMap
come argomento ed operare in modo analogo.
1 2 3 4 5 |
@RequestMapping( method = RequestMethod.GET ) public String showTodos( ModelMap model ) { model.addAttribute( "todos", service.getTodos() ); return "todos"; } |
L’utilizzo dell’annotazione @ModelAttribute complica leggermente la logica ma ha diverse conseguenze interessanti. Vediamo innanzitutto come ottenere lo stesso risultato attraverso tale annotazione:
1 2 3 4 5 6 7 8 9 10 |
@ModelAttribute public void addTodoList(Model model) { model.addAttribute( "todos", new ArrayList() ); } @RequestMapping( method = RequestMethod.GET ) public String showTodos(@ModelAttribute("todos") List<Todo> todos) { todos.addAll( service.getTodos() ); return "todos"; } |
L’annotazione a livello del metodo addTodoList()
indica al framework di invocarlo prima di ogni altro metodo del controller annotato con @RequestMapping. L’annotazione a livello di argomento del metodo showTodos()
, invece, indica a Spring di recuperare l’oggetto dal modello e se non presente di crearne una nuova istanza.
Una ulteriore possibilità per la restituzione diretta della vista è di ritornare un oggetto che implementa l’interfaccia View
. Spring MVC fornisce diverse implementazioni in funzione della View Technology utilizzata e di fatto sono tali oggetti che Spring istanzia quando è restituito il nome della vista. Rimanendo nel caso JSP/JSTL è possibile ottenere lo stesso risultato presentato negli esempi precedenti restituendo un oggetto di tipo JstlView
:
1 2 3 4 5 6 |
@RequestMapping( value="/{id}", method = RequestMethod.GET ) public View showTodo( ModelMap model, @PathVariable("id") Integer id ) { Todo todo = service.getTodo(id); model.addAttribute( "todo", todo ); return new JstlView( "/WEB-INF/views/todo.jsp" ); } |
In questo caso non interviene più il ViewResolver
quindi deve essere indicata esattamente la pagina jsp da renderizzare. Accenniamo brevemente anche alla possibilità di eseguire un redirect verso pagine esterne al sito restituendo l’oggetto RedirectView
:
1 2 3 4 |
@RequestMapping( value="/google", method = RequestMethod.GET ) public View showTodo() { return new RedirectView( "http://www.javaboss.it" ); } |
Restituzione di Modello e Vista
L’oggetto ModelAndView
consente al controller di restituire in unica soluzione sia il model che la vista da renderizzare.
1 2 3 4 5 6 7 |
@RequestMapping( method = RequestMethod.GET ) public ModelAndView showTodos() { ModelAndView model = new ModelAndView(); model.setViewName( "todos" ); model.addObject( "todos", service.getTodos() ); return model; } |
setViewName()
, con il quale si definisce il nome della vista, sia attraverso il metodo setView()
che invece ammette un oggetto di tipo View.
Restituzione del Model
Restituire il solo model significa ritornare un oggetto di tipo java.util.Map
oppure org.springframework.ui.Model
. La vista da renderizzare è determinata dal framework utilizzando lo specifico RequestToViewNameTranslator
. Ad esempio nel controller seguente il metodo showTodo()
restituisce una HashMap
con gli oggetti che compongono il model mentre la vista è determinata in base all’uri nell’annotazione @RequestMapping
. In particolare Spring MVC individuerà nella pagina /WEB-INF/niews/todos.jsp
la maschera da visualizzare.
1 2 3 4 5 6 7 8 9 10 |
@Controller @RequestMapping( value = "/todos" ) public class TodoController { @RequestMapping( method = RequestMethod.GET ) public Map<String, Object> showTodos() { Map<String, Object> model = new HashMap<String, Object>(); model.put( "todos", service.getTodos() ); return model; } } |
Altro
Esiste una varietà di altre possibilità che non discuteremo nell’articolo. Per maggiori dettagli si rimanda alla documentazione ufficiale al paragrafo Return Values.
Codice Sorgente
Il codice sorgente completo degli esempi presentati è scaricabile qui login-sprmvc.