ruddy palvair

Développeur Java/Js Craftsman

Validation de formulaire : spring boot et thymeleaf

Le 30 juin 2016 - Ruddy Palvair

 

Nous allons étudier comment utiliser la validation de formulaire avec spring boot et thymeleaf. Les sources sont disponibles sur mon github.

Presentation :

L’application est un simple formulaire avec deux champs « input » et un bouton. Les champs sont validés côté serveur et les erreurs sont récupérées pour être affichées à l’utilisateur. Pour avoir une bonne expérience utilisateur (ux) sans trop d’efforts, j’utilise bootstrap. Le design est minimaliste mais c’est voulu, on se concentre ici sur le processus de validation. Voici une image de l’application une fois lancée :

screen-app

Comme vous pouvez le voir l’application est très simple. Avant de commencer, jetons un coup d’oeil à l’organisation des sources du projet :

layout

Il y a un fichier Application.java qui est utlisé pour démarrer l’application dans votre IDE préféré(ici j’utilise Eclipse).

Ensuite il y a le controller HomeController.java et un java bean qui contient les informations pour le formulaire qui se nomme UserForm.java. C’est tout pour les classes Java, on peut difficilement faire plus simple hein ;-).

Interface :

Pour la partie interface, les fichiers résident dans le dossier resources/. Dans le sous-répertoire static/css, on retrouve un fichier css : style.css et 2 fichiers html dans le répertoire templates/: index.html et results.html. index.html est la page d’accueil de l’application et results.html est la page affichée quand le formulaire ne contient pas d’erreurs.

Vous pouvez consulter index.html ici. J’insisterai pour ma part sur l’élément form exposé ci-dessous :

<form action="#" th:action="@{/}" th:object="${userForm}" method="post">
  <div class="form-group" th:class="${#fields.hasErrors('name')}? has-error" >
  <label for="name">Name</label>
  <input type="text" th:field="*{name}" class="form-control" id="name" placeholder="name"/>
<span th:if="${#fields.hasErrors('name')}" th:errors="*{name}" th:errorclass="fieldError">Name Error</span>
  </div>
  <div class="form-group" th:class="${#fields.hasErrors('age')}? has-error">
   <label for="age">Password</label>
   <input type="text" th:field="*{age}" class="form-control" id="age" placeholder="age"/>
   <span th:if="${#fields.hasErrors('age')}" th:errors="*{age}" th:errorclass="fieldError">Age Error</span>
   </div>
   <button style="margin-top:10px" class="btn btn-default" type="submit">Submit</button>
</form>

Si l’on regarde cette ligne :

<form action="#" th:action="@{/}" th:object="${userForm}" method="post">

On remarque la présence du namespace thymeleaf th qui est référencé dans le fichier avec cette ligne :

<html xmlns:th="http://www.thymeleaf.org">

On utilise le dialect de thymeleaf avec les attributs th:action et th:object. Avec th:action, on poste le formulaire vers une url relative. Dans notre cas, il s’agit de l’url courante mais on pourrait en spécifier une différente

th:action="@{/someUrl}"

Avec l’attribut th:object on peut utiliser le java bean userForm en tant qu’objet courant du formulaire. Cet objet est créé dans le formulaire.

La partie la plus importante est le div qui contient les input du formulaire. Ils introduisent des attributs thymeleaf très intéressant :

 <div class="form-group"   th:class="${#fields.hasErrors('name')}? has-error" >
     <label for="name">Name</label>
     <input type="text" th:field="*{name}" class="form-control" id="name" placeholder="name"/>
     <span th:if="${#fields.hasErrors('name')}" th:errors="*{name}" th:errorclass="fieldError">Name Error</span>
 </div>

Une petite explication s’impose…

#fields est un objet thymeleaf qui retourne un booléen qui reflète la présence ou non d’erreurs de validation.

th:field permet de lier un la valeur dun élément html à une propriété d’un java bean

th:if a le même fonctionne que tag jsp c:if, il affiche l’élément concerné uniquement si la condition est respectée.

Les attributs th:errors et th:errorclass permettent de gérer l’affichage des erreurs.

Côté backend :

La classe Application.java est en charge de lancer l’application. J’utilise l’annotation @SpringBootApplication qui est une combinaison des annotations @Configuration, @EnableAutoConfiguration et @ComponentScan.

Le controller HomeController.java étend la classe WebMvcConfgurerAdpater.java. Cela nous permet de customiser l’application en redéfinissant certaines méthodes. Le bloc de code suivant permet de lier l’url results/ à la page results.jsp.

  @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/results").setViewName("results");
    }

La page d’accueil de l’application est le fichier index.jsp. Quand un utilisateur tape l’url de l’application soit localhost:8080 le navigateur renvoie la page index.jsp à l’utilisateur.  Dans la signature de la méthode home dans HomeController.java, on passe en paramètre un objet de type UserForm. Cet objet sera donc disponible dans la vue index.jsp. C’est pas beau ça?! Ce pattern est un raccourci à l’utilisateur de l’annotation @ModelAttribute.

@RequestMapping(value = "/", method = RequestMethod.GET)
    public String home(UserForm userForm) {
        return "index";
    }

La validation :

Comment fonctionne la validation de notre formulaire? En fait le plus gros du travail est effectué par Spring à l’aide du framework Hibernate Validator. On utilise simplement Hibernate Validator avec les annotations couplées à l’objet BindingResult de Spring. Voici la signature de la méthode qui valide le formulaire :

@RequestMapping(value = "/", method = RequestMethod.POST)
    public String validateUser(@Valid UserForm userForm, BindingResult bindingResult, RedirectAttributes redirectAttributes)

Avec l’annotation @Valid, on applique la validation sur l’objet de type UserForm qui est nous renvoyé lors de la soumission du formulaire.

L’objet de type BindingResult nous permet de savoir s’il y a des erreurs de validation et nous permet de les récupérer.

   if (bindingResult.hasErrors()) {
            log.error("errors = " + bindingResult.getAllErrors());
            return "index";
    }

L’interface RedirectAttributes permet de stocker des valeurs dans des scénarios ou la redirection est nécessaire.

Le mot de la fin :

Vous pouvez télécharger l’application et la lancer afin d’observer le mécanisme de validation mis en place. Si vous n’entrez pas de valeur correcte ou si vous laissez les champs vides vous devriez voir apparaître les erreurs de validation.

Suite au prochain épisode!