Création des formulaires en Spring

Formulaires web avec Spring et taglib spring-form.tld

Ce site ne sera plus alimenté de contenu après août 2014. Tous les nouveaux articles seront redigés pour www.waitingforcode.com

La partie précédente des articles consacrés au développement des applications web avec Spring concernait la couche des données. Cependant, pour avoir des données à manipuler, on doit pouvoir les rajouter dans le système. Le plus souvent on fera cela à travers les formulaires qui seront au cœur de cette partie.

"Flash" sessions

Avant d'aborder la problématique des formulaires, on verra comment transmettre les données d'une requête à l'autre dans les sessions.

Les données qui doivent être visibles uniquement pendant la transmission d'une requête à l'autre, s'appellent flash attributes. Il s'agit donc d'éléments stockés du côté du serveur qui sont disponibles dans le contrôleur invoqué après la redirection. On peut constater cela sur l'exemple suivant :

@RequestMapping("/question")
@Controller
public class QuestionController {
public String writeHandle(@ModelAttribute("question") @Validated({GeneralGroupCheck.class}) Question question, BindingResult binRes, 
    @LoggedUser AuthenticationFrontendUserDetails user, RedirectAttributes redAtt, @LocaleLang Lang lang) {
        logger.info("Received POST request " + question);
        if (binRes.hasErrors()) {
            redAtt.addFlashAttribute("error", true);
            redAtt.addFlashAttribute("question", question);
            redAtt.addFlashAttribute("errors", binRes);
        } else {
            try {
                question.setLang(lang);
                question.setSubscriber(conversionService.convert(user, Subscriber.class));
                questionService.addNew(question);
                redAtt.addFlashAttribute("success", true);
            } catch (Exception e) {
                binRes.addError(getExceptionError("question"));
                redAtt.addFlashAttribute("error", true);
                redAtt.addFlashAttribute("question", question);
                redAtt.addFlashAttribute("errors", binRes);
            }
        }
        return "redirect:/question/write";
    }
}

Ici la méthode addFlashAttribute() est chargée de passer des valeurs d'une requête à l'autre. Comme on peut constater, les flash attributes sont stockés selon la représentation key-value pair. Ces attributs sont importants car dans notre gestion des formulaires on va fonctionner selon le schéma suivant :

  1. 1. L'utilisateur remplit le formulaire et le soumet.
  2. 2. Une méthode d'un contrôleur traite cette requête. Ensuite, elle renvoie le résultat dans contrôleur chargé d'afficher la page avec le formulaire renseigné à la 1e étape.

Transformer un objet en formulaire sous Spring

Dans l'exemple évoqué dans le paragraphe précédent on a utilisé une nouvelle annotation, @ModelAttribute. Elle permet de présenter un objet dans une page web. L'objet présenté peut aussi bien être une entité qu'une classe du type Plain Old Java Object (POJO). La vue bénéficie de toutes les propriétés de l'objet présenté. Analysons maintenant deux exemples : un qui transformera l'entité en un formulaire et l'autre qui fera la même chose avec une classe prévue exprès pour le formulaire.

afficher le code

Et le fichier .JSP correspondant à ces fichiers :

<form:form modelAttribute="question" id="question" method="post">

<form:errors  />
  <form:label path="title">title</form:label>
  <form:input path="title" />
  <form:errors path="title" cssClass="error" />
  <form:label path="content">content</form:label>
  <form:textarea path="content" />
  <form:errors path="content" cssClass="error" />
  <form:errors path="subscriber" cssClass="error" />
  <form:errors path="lang" cssClass="error" />
  <button type="submit">Save</button>
  <button type="reset">Reset</button>
</form:form>

// TODO : modifier ce fichier de vue

Voici les classes spécifiques au formulaire qu'on souhaite afficher.

afficher le code

Et le fragment de la vue associée à cette classe :

<form:form modelAttribute="bookForm" id="bookForm" method="post">
<p>Alias : <form:input path="book.alias" />
	<form:errors path="book.alias" cssClass="error" /></p>
    <p><form:checkboxes items="${bookForm.categories}" path="categoriesChecked" itemValue="id" itemLabel="alias" />
	<form:errors path="categoriesChecked" cssClass="error" /></p>
    <p><form:checkboxes items="${bookForm.writers}" path="writersChecked" itemValue="id" itemLabel="fullname" />
	<form:errors path="writersChecked" cssClass="error" /></p>
</form:form>

Analysons maintenant le code de ces deux méthodes de création des formulaires sous Spring. Dans les deux on remarque la présence des tags <form:errors /> dans la partie de vue. Il s'agit des erreurs qui sont créées par le validateur dans la méthode chargée de traiter la requête envoyée. Pour pouvoir afficher ces erreurs au client, on doit se référer à des fonctions de traitement du formulaire (handleWrite() et addHandle()). Ces deux méthodes contiennent une ligne de code qui s'occupe de passer le résultat de validation dans la nouvelle requête via flash attributes. Il s'agit du fragment redAtt.addFlashAttribute("errors", binRes);. Ces résultats sont ensuite lisibles à travers les paramètres envoyés directement au modèle de vue, c'est-à-dire par l'implémentation de l'interface Model. Mais comment la vue sait de quelle erreur il s'agit ? Dans les deux contrôleurs, avant d'écrire nos méthodes, on spécifie un attribut addBinding. Il signale le nom du résultat de validation que va chercher <form:errors />.

D'ailleurs, les possibilités du taglib <form /> sont très puissantes. Tout d'abord, pour créer un champ du formulaire avec les données pré-remplies, il suffit de spécifier l'attribut path associé à la propriété de la classe du formulaire. Spring se chargera par la suite de récupérer la valeur et l'attribut au formulaire. Seule la création des champs gérant des collections s'avère plus compliquée. Dans notre cas, elle est illustrée par <form:checkboxes items="${bookForm.writers}" path="writersChecked" itemValue="id" itemLabel="fullname" /> :
- l'attribut items indique la collection des tous les éléments sélectionnables dans notre liste des inputs du type checkbox
- l'attribut path signifie des éléments déjà sélectionnés
- l'attribut itemValue signale quel champ de notre objet sera utilisé dans l'attribut value du checkbox généré
- l'attribut itemLabel réfère au champ qui jouera le rôle du libellé

Les deux exemples ont néanmoins une différence importante. Le deuxième, celui qui implémente une classe spécifique du formulaire, possède une méthode bindForm() annotée avec @InitBinder. Qu'est-ce qu'elle fait ? Elle identifie la méthode qui va initialiser WebDataBinder. Il s'agit d'un relieur qui traduit les éléments de la requête aux objets JavaBean. Dans l'exemple cité, binder enregistre des éditeurs qui s'occupent de transformer le texte reçu, séparé par des virgules en instances de la classe Category ou de la classe Writer. On va traiter cette thématique plus loin, dans l'article consacré à des property editors.

Bartosz KONIECZNY Formulaires

Une question ? Une remarque ?

*

*

Moi

Développeur d'applications Internet et journaliste passionné par l'adjectif français. Un aigle polonais orienté vers la progression, volant très haut et écoutant du zouk après les matches du foot français.

Vous appréciez mon travail ?

Pour contribuer au développement de ce site, ou pour remercier pour des articles rédigés, vous pouvez faire un don.

Un conseil PHP

Comment créer une signature pour OAuth

La création d'une signature (paramètre oauth_signature) pour la requête OAuth est réalisée avec l'algorithme HMAC-SHA1. Son implémentation chez PHP se présente ainsi :

hash_hmac('sha1', "Message to hashed", "Secret key", true)
Le résultat de cet algorithme doit ensuite être encodé avec Base64. Un exemple d'utilisation chez le protocle OAuth peut se présenter de la manière suivante :
public function getSignature($baseString, $signatureKey)
{
  return base64_encode(hash_hmac('sha1', $baseString, $signatureKey, true));   
}