Les controllers SPRING MVC

Cet article a pour but de passer en revue les controllers SPRING MVC les plus courants en fournissant une brève description de ceux-ci, de décrire dans quels cas ils sont utilisés et comment les mettre en place.

Le SimpleFormController

Le SimpleFormController est le controller standard fourni par Spring MVC. Il implémente comme tous les controllers l'interface Controller et met à disposition de son utilisateur toute une gamme de méthodes pour effectuer le traitement des formulaires basiques.
SimpleFormController permet de gérer :

  • Les GET pour la préparation et l'envoi du formulaire HTML.
  • Les POST pour l'envoi des données saisies au serveur.

SimpleFormController implémente un certain nombre de méthodes qui s'exécutent dans un certain ordre en fonction de la méthode qu'il appelle.
Dans le cas d'un GET les méthodes exécutées sont dans l'ordre:

formBackingObject -> initBinder -> showForm -> referenceData

formBackingObject: si elle n'est pas redéfinie, elle renvoie un objet de type commandClass défini dans la configuration. Si vous choisissez de la redéfinir vous devez renvoyer un objet qui permettra d'initialiser le formulaire (objet command). Si l'attribut session est placé à true, cet objet est placé en session

initBinder:cette méthode permet de déclarer les éditeurs de propriétés. Elle permet de convertir les propriétés de l'objet command de type non String en type String

showForm: c'est cette méthode qui va renvoyer l'objet commandClass au formulaire sous le nom commandName prédéfini dans la configuration

referenceData: Cette méthode est appelée par showForm, elle permet d'envoyer au formulaire un dictionnaire de classes qui permet d'envoyer au formulaire des objets autres que commandClass, si besoin est.
Une fois que le formulaire a été saisi et que celui-ci a été validé, le controller doit être appelé par la méthode POST. Cette fois ci les méthodes appelées sont dans l'ordre:

formBackingObject-> onBind-> validateOnBinding-> onBindAndValidate-> onSubmit

formBackingObject: la méthode permet de créer l'objet qui va permettre l'enregistrement des données du formulaire au cas où la propriété session est à false. Si celle-ci est placée à true alors l'objet est récupéré dans la session. Les valeurs saisies sont placées dans les propriétés de commandClass du même nom. Si des éditeurs avaient été déclarés précédemment ils auraient été appelés pour effectuer la conversion de type inverse String->type

onBind:par défaut cette méthode ne fait rien. Elle peut être redéfinie pour effectuer certaines conversions manuellement.

validateOnBinding: cette méthode renvoie un booléen qui s'il est égal à true permet l'appel d'un objet validator pour la validation des données saisies.

onBindAndValidate:par défaut cette méthode ne fait rien. Elle peut néanmoins servir pour effectuer des validations de saisie sans objet validator.

onSubmit: Il existe plusieurs surcharges de la méthode onSubmit qui appelle elle même la méthode doSubmitAction. Le traitement des formulaires étant spécifique vous serez amenés à choisir une de ces méthodes pour la redéfinir en fonction de vos besoins. Votre choix se portera selon la nature des objets que vous souhaitez utiliser. Vous pourrez ensuite renvoyer selon vos besoins une collection d'objets (Map) associée à une vue ou tout simplement une vue (successView)

Tout ceci peut paraître un peu compliqué, mais dans la pratique vous n'aurez à redéfinir, sauf cas extrême, que 2 ou 3 de ces méthodes.
Pour pouvoir utiliser un controller, il va falloir le déclarer dans vos fichiers de configuration (sauf si vous choisissez d'utiliser les annotations). Dans ce cas vous devrez le déclarer suivant la forme suivante:

<bean  id="creerPickingController" class="com.netapsys.controller.CreerPickingController">
           <property name="formView" value="creerPicking" /> 
           <property name="successView" value="creerPickingSuccess" /> 
           <property name="commandName" value="creerPickingInfo"/> 
           <property name="commandClass" value="com.netapsys.view.impl.CreerPickingInfo"/> 
           <property name="creerPicking" ref="creerPickingBean" />
           <property name="validator" ref="creerPickingValidator" />
</bean>

id: nom que vous donnerez à votre controller dans le contexte Spring
class:nom pleinement qualifié de la class controller
formview:formulaire pour lequel le controller sera appelé (obligatoire)
successView: page qui sera affichée si le formulaire est convenablement saisi et validé (optionnel)
validator: classe de validation qui permettra de valider le formulaire avant que celui-ci soit traité par le controller (optionnel)

Toutes les autres propriétés correspondent à des classes injectées dans le controller.

Le MultiActionController

Le MultiActionController est un controller spring mvc qui permet le traitement de plusieurs requêtes par un seul et unique controller. Il permet de mutualiser les pages qui nécessitent un traitement qui pourrait être plus ou moins similaire et de réduire la multiplication des controllers Spring.
MultiActionController est une classe abstraite et doit par conséquent être dérivée. Deux requêtes différentes pourront alors être traitées par une méthode ayant la signature suivante:

ModelAndView actionName(HttpServletRequest request, HttpServletResponse response);

actionName est le nom de la méthode. Chaque requête peut être traitée par une méthode différente.
Alors, comment le MultiActionController peut-il savoir la méthode correspondant à une requête donnée? Le MultiActionController utilise pour cela un resolver qui permettra de mapper une requête sur le traitement correspondant.
Voici un exemple de mapping:

La déclaration du controller se fait de la manière suivante:

<bean id="dispatcherController"	class="com.netapsys.controller.DispatcherController">
		<property name="methodNameResolver">
			<ref local="dispatcher" />
		</property>%%%
		<property name="articleManager" ref="articleManager" />
</bean>

Comme tous les controllers Spring mvc, la déclaration du bean comporte un attribut id qui correspond au nom du bean dans le contexte Spring
et un attribut class qui correspond au nom pleinement qualifié du controller. Vous trouvez également la présence des beans injectés dans le controller. La spécificité de ce controller réside dans la propriété "méthodeNameResolver" qui référence un resolver de nom qui a pour fonction de lier les noms des requêtes aux méthodes correspondantes. Dans le cas présent le resolver a pour nom "dispatcher" (voir ci-dessous).

<bean id="dispatcher" class="org.springframework.web.servlet.mvc.multiaction.ParameterMethodNameResolver">%%%
 
                <property name="defaultMethodName">
			<value>ajouter</value>
		</property>
 
		<property name="methodParamNames">
			<list>
 
				<value>ajouter</value>
				<value>deplacer</value>
				<value>supprimer</value>
			</list>
		</property>
</bean>

Le code ci-dessus est la déclaration du resolver de noms de spring-mvc.

id:nom du resolver dans le contexte spring. Bien entendu, celui-ci doit être le même que celui qui a été déclaré précédemment dans votre controller

class:nom pleinement qualifié de la classe spring-mvc qui servira de resolver de nom

Les propriétés:

defaultMethodName: méthodes qui vont être exécutées si aucun nom de méthode ne correspond au nom de la requête émise. Vous pouvez en déclarer plusieurs mais généralement seule la première est exécutée.

methodParamNames:liste des noms des méthodes déclarées dans le MultiActionController. L'ordre n'a pas grande importance, seul le nom prime.

Remarque: il existe d'autres resolvers, celui décrit ci-dessus étant le plus simple à mettre en place. Vous pourrez donc en choisir un autre si besoin.

Voici un aperçu de la classe qui correspond au mapping précédent:

public class DispatcherController extends MultiActionController
{
 
    public ModelAndView ajouter(final HttpServletRequest request,
            final HttpServletResponse response)
	{	 ...	
	}
 
   public ModelAndView deplacer(final HttpServletRequest request,
            final HttpServletResponse response)
	{	 ...	
	}
 
    public ModelAndView supprimer(final HttpServletRequest request,
            final HttpServletResponse response)
    {  … 
	}
	…
}

Donc pour le controller DispatcherController, la méthode "deplacer" ne sera exécutée que si et seulement si l'une des jsp mappées à ce controller contient un bouton de type submit qui est activé. Idem pour la méthode "supprimer". Seule la méthode "ajouter" pourra être exécutée dans 2 cas:

  • Si une requête correspond à son nom
  • Si une requête ne correspond au nom d'aucune méthode étant donné que c'est la méthode exécutée par défaut (defaultMethodName)

Remarque: il est conseillé d'utiliser le MultiActionController uniquement pour les pages qui ne font qu'afficher des informations. Les procédés de validation dans celui-ci étant fastidieux et complexe, il est déconseillé de s'en servir pour les formulaires nécessitant une validation.

AbstractWizardFormController

Ce controller permet de construire des formulaires formés de plusieurs pages. Tout comme le MultiActionController, l'AbstractWizardFormController est une classe abstraite et doit donc être dérivée.
AbstractWizardFormController dérive de AbstractFormController, c'était déjà le cas du SimpleFormController. On trouvera donc des similarités.
Voici un exemple de mapping:

<bean id="editionPaletteController" class="com.netapsys.controller.AuteurController">		
 
		<property name="commandName" value="auteurInfo"/>
		<property name="commandClass" value="com.netapsys.view.impl.AuteurInfo"/>			
 
		<property name="pages">
			<list>
				<value>saisirNom</value>
				<value>saisirPrenom</value>
			</list>
		</property>
		<property name="allowDirtyBack" value="true" />
		<property name="allowDirtyForward" value="true" />
 
		<property name="auteur" ref="auteur" />
 
</bean>

Comme pour chaque controller on trouve les attributs id et class qui correspondent toujours au nom de la classe dans le contexte Spring et au nom pleinement qualifié de celle-ci. Les propriétés commandName, commandClass sont les mêmes que pour le SimpleFormController.

Les autres propriétés sont:

pages: listes des pages qui vont contenir le formulaire multipages sans l'extension. L'ordre est extrêmement important étant donné qu'il déterminera l'ordre d'affichage.

allowDirtyBack et allowDirtyForward: lors du passage d'une page à une autre, l'AbstractWizardFormController procède toujours à une validation, quel que soit le sens de navigation dans les pages. La propriété allowDirtyBack contrôle la validation arrière et allowDirtyForward contrôle la validation de la navigation avant. Ces 2 propriétés sont à true par défaut. Placé à false la validation lors de la navigation est désactivée. Dans ce cas, la validation n'est effectuée que lors de la validation définitive du formulaire à la dernière page de celui-ci.

Bien entendu vous trouverez également la liste de toutes les classes injectées dans le controller.

Voici un aperçu du code de la classe mappée ci-dessus

public class AssistantController extends AbstractWizardFormController {
 
      //Methode qui permet la validation définitive du formulaire associé
      protected ModelAndView processFinish(HttpServletRequest request, HttpServletResponse response, Object command, BindException   
      errors) throws Exception {
 
        ..............
     }
 
     //Methode permettant l'annulation de toutes les saisies
     protected ModelAndView processCancel(HttpServletRequest request, HttpServletResponse response, Object command, BindException errors)
     throws Exception {
 
        ............
 
    }
 
    //Méthode permettant la validation page à page du formulaire
    protected void validatePage(Object formulaire, Errors errors, int page) {
 
       ......
 
   }
 
 
 
}

Une fois le mapping achevé, il reste néanmoins un dernier détail. Il faut indiquer dans les jsp les traitements souhaités.
Pour cela il faut définir certains champs dans les formulaires de chaque page :

<input type="hidden" name="_page" value="x">

Le champ _page permet d'indiquer à chaque jsp quel est son ordre dans le formulaire. Pour cela il faut définir x en sachant que la première page a toujours le numéro 0.

<input type="submit" value="Suivant" name="_targetx">

Dans chaque page (sauf la dernière du formulaire) il faut nommer le bouton submit "_targetx", x étant le numéro de la page à atteindre.
Cela vous permettra de naviguer dans le formulaire dans n'importe quel sens. Il suffit pour cela de définir x avec un nombre plus petit que le numéro de la page courante pour naviguer vers l'arrière et de définir x avec un nombre plus grand pour naviguer vers l'avant.

Remarque: vous n'êtes pas obligés de passer directement à la page suivante, vous pouvez sauter des pages si besoin et passer par exemple de la page 1 à la page 3 sans passer par la 2.

<input type="submit" value="Annuler" name="_cancel">

Vous pouvez placer si vous le souhaitez dans chaque page un bouton _cancel qui fera appel à la méthode processCancel du controller et qui par défaut annule la saisie du formulaire.

<input type="submit" value="Valider" name="_finish">

Arrivé à la dernière page du formulaire, on envoie les données au serveur via le bouton "_finish". Celui-ci fait appel à la méthode processFinish du controller qui procède au traitement des données après validation par la méthode validatePage.

Remarque: la page affichée à la fin de la validation finale (équivalent du successView) ne doit pas être dans la liste des pages référencées par le controller. Celle-ci peut être définie soit par une propriété injectée soit directement dans la classe.

Cette brève description devrait vous aider à savoir quel controller utiliser. J'espère vous avoir donné une idée des possibilités qui vous sont offertes par ces controllers. Néanmoins vous pourrez également si le besoin s'en ressent être amenés à utiliser d'autres controllers qui ne sont pas présentés ici.

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Captcha *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.