Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial

View e i ViewResolver

Link copiato negli appunti

View e i ViewResolver in Spring MVC

Come abbiamo visto nel capitolo precedente i controller delegano alle viste il compito di mostrare all'utente i contenuti in maniera presentabile e stilisticamente appropriata. Quando un controller ritorna una stringa (che non inizi con redirect o forward) o un oggetto ModelAndView, significa che esso vuole delegare al motore interno di Spring MVC la creazione di una vista identificata dal valore della stringa ritornata (o incapsulata nell'oggetto) sulla base dei parametri inseriti nel Model.

Schematizzando possiamo riassumere che una View identifica una particolare risorsa in grado di mostrare all'utente dei dati (per esempio una classica JSP) mentre un ViewResolver permette di identificare una determinata View sulla base di una stringa. Spring MVC non presenta nessun ViewResolver di default. Nel capitolo precedente ottenevamo degli errori ritornando delle stringhe nei controller implementati.

L'InternalResourceViewResolver

Il caso più classico di ViewResolver è rappresentato dall'InternalResourceViewResolver che permette di identificare una View sulla base di risorse interne al progetto come ad esempio JSP. Per inserire nel nostro contesto un InternalResourceViewResolver basta creare un bean:

<bean id="viewResolver" class="org.springframework.web.servlet.view.UrlBasedViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value="/WEB-INF/view/"/>
<property name="suffix" value=".jsp"/>
</bean>

In questo modo abbiamo definito il nostro ViewResolver che sarà in grado di recuperare la JSP concatenando le stringhe /WEB-INF/view e .jsp al valore della stringa ritornata. Il parametro viewClass rappresenta la classe da cui ciascuna vista verrà istanziata. Se per esempio il nostro controller dovesse tornare home il ViewResolver cercherebbe la JSP in /WEB-INF/view/home.jsp. Inoltre se nel metodo handler viene istanziato o modificato un oggetto Model (o ModelAndView) esso viene passato alla JSP.

Analizziamo un esempio:

@RequestMapping(value="/pagina-personale", method=RequestMethod.GET)
    public String paginaPersonale(Model model) {
    model.addAttribute("nome", "Alberto");
    model.addAttribute("cognome", "Bottarini");
    return "pagina-personale";
}

In questo caso i parametri aggiunti all'oggetto Model vengono resi disponibili nella JSP.

<h1>Ciao <c:out value="${nome }" /> <c:out value="${cognome}" /></h1>

Altri esempi di ViewResolver

Oltre all'InternalResourceViewResolver Spring MVC ci mette a disposizione altri Resolver tra cui i
principali sono:

Resolver Descrizione
XmlViewResolver Identifica la view corretta a partire da un file XML di configurazione.
ResourceBoundleViewResolver Identifica la view corretta a partire da chiavi contenuto nel resource boundle (utile per offrire viste localizzate).
UrlBasedViewResolver Identifica la view corretta trasformando il nome logico della vista in un URL (l'InternalResourceViewResolver è una sottoclasse di UrlBasedViewResolver).
ContentNavigationViewResolver Identifica la view sulla base del filename richiesto o in base al parametro Accept della request.
BeanNameViewResolver Identifica la view sulla base del nome del bean all'interno del contesto di Spring MVC.

Il ContentNavigationViewResolver

Uno dei risolver più interessanti è ContentNavigationViewResolver che rappresenta una sorta di collezione di sotto-resolver ed è in grado di differenziarne l'invocazione sulla base di due parametri:

  • contenuto dell'header Accept della request
  • estensione del file richiesto

Dato che difficilmente via browser è possibile gestire il valore di Accept, prenderemo in
considerazione la seconda opzione.

<bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
	<property name="mediaTypes">
		<map>
			<entry key="atom" value="application/atom+xml"/>
			<entry key="html" value="text/html"/>
			<entry key="json" value="application/json"/>
		</map>
	</property>
	<property name="viewResolvers">
		<list>
			<bean class="org.springframework.web.servlet.view.BeanNameViewResolver"/>
			<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
				<property name="prefix" value="/WEB-INF/jsp/"/>
				<property name="suffix" value=".jsp"/>
			</bean>
		</list>
	</property>
	<property name="defaultViews">
		<list>
			<bean class="org.springframework.web.servlet.view.json.MappingJacksonJsonView" />
		</list>
	</property>
</bean>

Grazie a questo esempio preso direttamente dalla documentazione ufficiale è possibile registrare dei Content-Type a partire dall'estensione del file (in questo caso atom, html, json) e dei ViewResolver. Per ciascuna richiesta il ContentNegotiatingViewResolver identifica il Content-Type e il ViewResolver in grado di gestire la chiamata. Nel caso in cui ne trovi uno, delegherà ad esso la gestione, altrimenti si affiderà alla View di default.

Nota: per poter utilizzare questo esempio è necessario importare nel progetto la dipendenza Jackson.

ViewResolver e View personalizzate

Un'applicazione può presentare diverse tipologie di contenuti, quindi diverse viste e quindi diversi ViewResolver. Tramite una configurazione mirata è possibile gestire ViewResolver multipli, organizzandoli in una sorta di serie ordinata dalla quale Spring MVC cercherà il resolver più adatto. Data la vastissima estendibilità di Spring, è inoltre possibile definire delle View personalizzate definendo puntualmente quale deve essere il loro compito. Nel nostro esempio creeremo una vista che si appoggierà a OpenCSV per generare appunto dei CSV.

Dopo aver incluso le librerie necessarie nel pom.xml di Maven, creiamo la classe CSVView:

@Component
public class CSVView extends AbstractView {@Override
	protected void renderMergedOutputModel(Map model, HttpServletRequest request, HttpServletResponse response) throws Exception {
		CSVWriter writer = new CSVWriter(response.getWriter());
		String[] names = (String[]) model.get("names");
		writer.writeNext(names);
		writer.close();
	}
}

Tra gli aspetti importanti è necessario ricordare che la classe deve essere decorata con l'annotation @Component in modo che Spring la consideri parte del proprio contesto e che deve estendere AbstractView e quindi implementare il metodo void renderMergedOutputModel. In questo metodo non facciamo altro che recuperare i dati dal model (che sarà popolato nel controller) e scrivere, grazie alla classe CSVWriter di OpenCSV, il contenuto della response.

Una volta definita la nostra CSVView è necessario includere nel WebApplicationContext un ViewResolver in grado di gestire le nuove classi che verranno istanziate. In questo caso utilizziamo un BeanNameViewResolver che associerà la CSVView al nome logico CSVView (ricordiamo che i bean identificati dall'annotation @Component di default acquisiscono come id il nome stesso della classe).

<bean class="org.springframework.web.servlet.view.BeanNameViewResolver">
<property name="order" value="1" />
</bean>

Dato che ora nel WebApplicationContext saranno presenti due diversi View esolver, è necessario aggiungere in almeno uno di essi la proprietà order per identificarne la priorità. Impostando "1" come order del nuovo BeanNameViewResolver significherà che Spring MVC cercherà innanzitutto di utilizzare quest'ultimo come resolver e in caso di insuccesso passerà al precedente UrlBasedViewResolver.

Ora basterà creare un controller che ritornerà CSVView come nome logico della vista:

@RequestMapping(value="/csv", method=RequestMethod.GET)
public String getCSV(Model model) {
    String[] names = new String[] { "Alberto", "Luca", "Claudio" };
    model.addAttribute("names", names);
    return "CSVView";
}

Ti consigliamo anche