Spring Web Flow : binding e validazione

Prosegue la nostra serie su Spring Web Flow. In questo articolo saranno trattati nuovi concetti di SWF, quali il binding e la validazione dei bean.

Binding

In Spring Web Flow il binding con i model è un’operazione piuttosto semplice e immediata. Nel nostro esempio abbiamo creato 3 nuovi JavaBean, che andremo poi a legare alle nostre pagine jsp attraverso il file di definizione del flusso. I nuovi bean saranno: Book, Author e Address.

Come si può notare l’oggetto Book è un semplice java bean con attributi private e dei metodi accessori. E’ importante notare una caratteristica del binding: i nomi degli attributi rispecchiano esattamente i parametri che Web Flow troverà come parametri della request, una volta effettuato il submit del form (come visibile dal codice seguente).

Per far in modo che i dati del form vengano automaticamente legati al nostro bean, si deve effettuare la configurazione nel file di definizione del flusso

La prima cosa che notiamo è l’aggiunta dell’attributo model nel view-stateenterBookData. Questo attributo permette di definire l’oggetto che verrà legato alla nostra view, bookData, definizione che troviamo nel file di configurazione di Tiles (vedi prima parte della serie di articoli). SWF per applicare il binding fa uso del metodo shouldBind() della nostra AbstractMvcView, classe base per le View di SpringMVC (sia per le servlet che per le portlet).

Il metodo non farà altro che verificare l’esistenza dell’attributo bind e del suo valore boolean. Nel caso in cui questo attributo non venga aggiunto alla nostra transition (nel nostro esempio la transition “save”), il bind verrà applicato. In caso il boolean sia valorizzato a true, il framework si occuperà di effettuare il binding tra i parametri della request e gli attributi dell’oggetto definito.

Da notare che l’id book utilizzato come valore dell’attributo model, è stato precedentemente definito come variabile visibile all’interno dell’intero flusso attraverso l’uso del tag var. Questo elemento permette di definire una variabile con scope valido per l’intero flusso. Gli oggetti che vengono salvati all’interno dello scope flow devono obbligatoriamente implementare l’interfaccia java.io.Serializable.

Il tag var ci permette di definire il name con cui verrà salvato l’oggetto all’interno del flow scope e la class che indica quale oggetto il framework deve creare nello scope. Oltre a quanto mostrato nell’esempio, è possibile indicare nell’attributo model qualsiasi oggetto all’interno dei seguenti scope:

  • flowScope: viene creato nel momento in cui un flow ha inizio e viene distrutto quando termina il flusso
  • viewScope: viene creato nel momento in cui il flusso entra in un view-state e termina quando esce dallo stato
  • requestScope: viene creato nel momento in cui viene creato uno state del flusso e distrutto al suo ritorno
  • flashScope: viene creato nel momento in cui un flow ha inizio, viene ripulito in ogni view renderer, e viene distrutto quando il flusso termina
  • conversationScope: viene creato quando il top-level flow ha inizio e termina quando il top-level flow termina. E’ valido anche per l’esecuzione dei sotto flussi.

 

Se nessuno scope viene definito per l’attributo model, come nel nostro esempio, lo scope viene automaticamente selezionato dal framework che ricerca nel seguente ordine: request, flash, view, flow e conversation. Nel caso non venga trovato l’attributo in nessuno degli scope precendenti, viene sollevata un’eccezione del tipo EvaluationException.

Analogamente a quanto abbiamo fatto per lo state enterBookData, anche per lo state enterAuthorData abbiamo definito il model author che verrà utilizzato per il binding automatico delegato al framework. Come detto in precedenza, le variabili definite attraverso l’uso del tag var hanno una visibilità per tutto il life-cycle del flow scope, pertanto hanno una durata pari alla durata del nostro flusso (ricordate però che non sono visibili all’interno dei sub-flow, per quello va usato lo scope conversationScope).

Questo ci permette di poter accedere ai nostri oggetti salvati nel flow scope, anche in pagine diverse da quelle usate per effettuare il binding. A dimostrazione di ciò, abbiamo opportunamente modificato l’ultima pagina del nostro flusso, la confirmationPage.jsp, per dimostrare come sia possibile accedere alle informazioni inserite in maschere precedenti.

Eseguendo il nostro flusso di esempio, possiamo notare come le informazioni salvate nei primi due state, enterBookData e enterAuthorData, le ritroviamo nello state finale confirmation. Questo è possibile perchè abbiamo salvato gli oggetti book e author nello scope del flusso, se le avessimo rese disponibili solo al viewScope, ad esempio, non avremmo potuto accedere in nessun modo, nell’ultima pagina, ai valori degli oggetti Author e Book.

Riepilogo finale

Riepilogo finale

E’ possibile fare in modo che il binding non venga effettuato dal framework, tramite l’attributo bind=”false”. In questo modo, per particolari eventi, come ad esempio un’uscita da una pagina, il binding e la validazione (se presente) non vengono eseguiti. Di default l’attributo vale true.

 

Validazione

Unitamente al processo di binding, Spring Web Flow mette a disposizione anche la validazione. E’ possibile effettuare la validazione dei nostri model sia implementando un metodo di validazione all’interno del nostro model sia implementando una classe che avrà come compito esclusivo la validazione.

Oltre a queste due possibilità, SWF offre la possibilità di utilizzare qualsiasi framework esterno di validazione. Vediamo come implementare la validazione dei nostri bean Book e Author utilizzando entrambe le tecniche messe a disposizione dal framework.

 

Implementazione del metodo nel model

In questa prima implementazione andremo a modificare il nostro bean Book. Rispettando delle semplici convenzioni imposte da SWF creiamo un nuovo metodo, all’interno del nostro bean, che si chiamerà validateEnterBookData. La convenzione da seguire per questo tipo di approccio alla validazione con Web Flow, è quella di creare un metodo public void del tipo validate${state}, dove ${state} corrisponde all’id del nostro view-state.

Seguendo questa semplice regola abbiamo creato il nostro metodo come segue

La classe org.springframework.binding.message.MessageBuilder che abbiamo utilizzato nel codice di validazione, ci permette di creare dei messaggi del tipo: error, warning, fatal e info. E’ possibile utilizzare un testo di default, come nel nostro esempio, attraverso il metodo defaultText(String text) o utilizzare l’internazionalizzazione tramite i metodi code(String code) e codes(String[] codes). La stringa che utilizzeremo come code, sarà la chiave da ricercare nel nostro file .properties d’internazionalizzazione.

Infine, una volta preparato il messaggio, invochiamo il metodo build() che crea il messaggio che verrà poi visualizzato. Tutti i messaggi vengono conservati all’interno di un org.springframework.util.CachingMapDecorator nel MessageContext, fornito al metodo validate dal framework.

Una volta terminato il processo di validazione, nel caso ci siano degli errori presenti all’interno del MessageContext, SWF come view presenta la stessa pagina ricaricata e la gestione dell’errore è demandata allo sviluppatore. SpringMVC mette inoltre a disposizione delle tag library per la gestione degli errori.

Validazione a livello di metodo

Validazione a livello di metodo

 

Implementazione della classe di validazione

Per il secondo tipo d’implementazione abbiamo creato una nuova classe Validator, il cui compito sarà esclusivamente quello di effettuare la validazione del nostro model. Anche in questo caso si devono seguire delle convenzioni stabilite dal framework.

Per questo tipo d’implementazione si deve creare una classe con il nome del tipo ${model}Validator per essere automaticamente caricato e invocato. La seconda convenzione è quella di creare un metodo per ogni view-state che vogliamo validare. Come nell’implementazione che abbiamo mostrato precedentemente, anche in questo caso il nome del metodo sarà del tipo validate${state}. Pertanto nel nostro esempio il validator avrà il nome AuthorValidator e il nome del metodo sarà validateEnterAuthorData

Come possiamo notare l’implementazione del metodo è del tutto simile a quella vista precedentemente. In questo estratto di codice, due sono le differenze rispetto alla versione precedente di validazione e sono: l’uso dell’annotation @Component e il model che viene passato nel metodo di validazione.

L’annotazione org.springframework.stereotype.Component, ci permette d’indicare al container di Spring che la nostra classe validator è un componente. Tale classe verrà, tramite configurazione, automaticamente individuata e caricata dal container.

La seconda differenza, consiste nel model passato nel nostro metodo validate. Questo ci permette di poter accedere ai valori del nostro bean nel codice di validazione e poter verificare gli attributi del model utilizzato per il binding.

Validazione con una classe

Validazione con una classe

 

Conclusioni

In questo secondo articolo d’introduzione a Spring Web Flow ci siamo occupati di due concetti come binding e validazione che in ogni applicazione sono operazioni di routine e piuttosto ripetitive. SWF rende queste operazioni molto semplici e immediate purchè si seguano alcune piccole convenzioni che rendono la vita più semplice allo sviluppatore che affronta queste tematiche.

Nella versione 2.0.3 di Web Flow, appena uscita, sono state aggiunte alcune piccole novità , come:

  • possibilità di poter scegliere quali attributi del model mappare con i parametri di un form
  • rendere alcuni attributi obbligatori oppure opzionali
  • fare conversioni a un certo tipo di dato direttamente nel processo di binding
  • possibilità di evitare la validazione senza escludere il binding

 

Per ulteriori informazioni vi rimandiamo alla documentazione ufficiale.

Link al primo articolo della serie

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *