devstory

Créer une application d'enregistrement d'utilisateur avec Spring Boot, Spring Form Validation

  1. Objectifs d'exemple
  2. Créer  le projet Spring Boot
  3. Configurer le fichier pom.xml
  4. Security, MessageSource
  5. Model, DAO
  6. Form Bean, Validator
  7. Controller
  8. Thymeleaf Template
  9. Exécuter l'application

1. Objectifs d'exemple

Le document est basé sur :
  • Eclipse 3.7 (Oxygen)

  • Spring Boot 2.x

  • Spring Validation

  • Thymeleaf

Dans cet article, je vais vous montrer comment créer une application d'enregistrement d'utilisateur à l'aide de Spring Boot + Spring Validation + Thymeleaf. Les sujets mentionnés dans cette publication contiennent:
  • Créez un Form d'enregistrement sur Spring.
  • Utilisez Spring Validator afin de valider (validate) les informations que l'utilisateur saisit.
  • Expliquez la principe de fonctionnement du Spring Validator.
Avant-première l'application :

2. Créer  le projet Spring Boot

Sur Eclipse créez un projet Spring Boot.
Saisissez :
  • Name: SbRegistrationFormValidationThymeleaf
  • Group: org.o7planning
  • Description: Spring Boot + Form Validation + Thymeleaf
  • Package: org.o7planning.sbformvalidation
Sélectionnez les technologies et les bibliothèques qui seront utilisées :
  • Security
  • Validation
  • Web
  • Thymeleaf
OK, le projet a été créé.
SbRegistrationFormValidationThymeleafApplication.java
package org.o7planning.sbformvalidation;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SbRegistrationFormValidationThymeleafApplication {

    public static void main(String[] args) {
        SpringApplication.run(SbRegistrationFormValidationThymeleafApplication.class, args);
    }
}

3. Configurer le fichier pom.xml

Dans cet exemple, nous allons utiliser la bibliothèque Commons Validation afin de vérifier si l'email que l'utilisateur a saisi est e exact ou non. Par conséquent, il est nécessaire de déclarer cette bibliothèque dans le fichier pom.xml.
** Commons Validation **
<dependencies>
    .....

        <!-- https://mvnrepository.com/artifact/commons-validator/commons-validator -->
    <dependency>
        <groupId>commons-validator</groupId>
        <artifactId>commons-validator</artifactId>
        <version>1.6</version>
    </dependency>

        .....    
</dependencies>
Le contenu complet du fichier pom.xml:
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
             http://maven.apache.org/xsd/maven-4.0.0.xsd">
             
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.o7planning</groupId>
    <artifactId>SbRegistrationFormValidationThymeleaf</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>SbRegistrationFormValidationThymeleaf</name>
    <description>Spring Boot + Form Validation + Thymeleaf</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.0.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        
        <!-- https://mvnrepository.com/artifact/commons-validator/commons-validator -->
        <dependency>
            <groupId>commons-validator</groupId>
            <artifactId>commons-validator</artifactId>
            <version>1.6</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

4. Security, MessageSource

Dans cet exemple, nous ne nous concentrons pas à la sécurité d'application. Pourtant, nous devons accéder la bibliothèque Spring Security pour coder (encode) le mot de passe d'utilisateur avant de l'enregistrer sur la base de données. Et vous devriez déclarer un Spring BEAN pour le codage le mot de passe.
WebSecurityConfig.java
package org.o7planning.sbformvalidation.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

	@Bean
	public PasswordEncoder passwordEncoder() {
		BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
		return bCryptPasswordEncoder;
	}

	// In this example we do not use Security.
	// Override this method with empty code
	// to disable the default Spring Boot security.
	@Override
	protected void configure(HttpSecurity http) throws Exception {
		// Empty code!
	}
}
Dans cet exemple, nous avons le fichier validation.properties. Ce fichier comprend des codes d'erreur (Error code), utilisés pour avertir des utilisateurs lorsqu'ils saisissent les informations incorrectes.
validation.properties
NotEmpty.appUserForm.userName=User name is required
NotEmpty.appUserForm.firstName=First Name is required
NotEmpty.appUserForm.lastName=Last name is required
NotEmpty.appUserForm.email=Email is required
NotEmpty.appUserForm.password=Password is required
NotEmpty.appUserForm.confirmPassword=Confirm Password is required
NotEmpty.appUserForm.gender=Gender is required
NotEmpty.appUserForm.countryCode=Country is required

Pattern.appUserForm.email=Invalid email
Duplicate.appUserForm.email=Email has been used by another account
Duplicate.appUserForm.userName=Username is not available
Match.appUserForm.confirmPassword=Password does not match the confirm password
Vous devez déclarer un MessageResource Spring Bean, pourque Spring télécharge (load) automatiquement le contenu du fichier validation.properties dans la mémoire.
WebConfiguration.java
package org.o7planning.sbformvalidation.config;

import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ReloadableResourceBundleMessageSource;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
@EnableWebMvc
public class WebConfiguration implements WebMvcConfigurer {

	@Bean
	public MessageSource messageSource() {
		ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
		// Load file: validation.properties
		messageSource.setBasename("classpath:validation");
		messageSource.setDefaultEncoding("UTF-8");
		return messageSource;
	}

}

5. Model, DAO

La classe AppUser représente d'un enregistrement (record) du tableau APP_USER. Elle est une utilisatrice (user) enregistrées avec succès en système.
AppUser.java
package org.o7planning.sbformvalidation.model;

public class AppUser {
    

    private Long userId;
    private String userName;
    private String firstName;
    private String lastName;
    private boolean enabled;
    private String gender;
    private String email;
    private String encrytedPassword;
    
    private String countryCode;

    public AppUser() {

    }

    public AppUser(Long userId, String userName, String firstName, String lastName, //
            boolean enabled, String gender, //
            String email,String countryCode, String encrytedPassword) {
        super();
        this.userId = userId;
        this.userName = userName;
        this.firstName = firstName;
        this.lastName = lastName;
        this.enabled = enabled;
        this.gender = gender;
        this.email = email;
        this.countryCode= countryCode;
        this.encrytedPassword = encrytedPassword;
    }

    public Long getUserId() {
        return userId;
    }

    public void setUserId(Long userId) {
        this.userId = userId;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public boolean isEnabled() {
        return enabled;
    }

    public void setEnabled(boolean enabled) {
        this.enabled = enabled;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getEncrytedPassword() {
        return encrytedPassword;
    }

    public void setEncrytedPassword(String encrytedPassword) {
        this.encrytedPassword = encrytedPassword;
    }

    public String getCountryCode() {
        return countryCode;
    }

    public void setCountryCode(String countryCode) {
        this.countryCode = countryCode;
    }

}
Country.java
package org.o7planning.sbformvalidation.model;

public class Country {

    private String countryCode;
    private String countryName;

    public Country() {

    }

    public Country(String countryCode, String countryName) {
        this.countryCode = countryCode;
        this.countryName = countryName;
    }

    public String getCountryCode() {
        return countryCode;
    }

    public void setCountryCode(String countryCode) {
        this.countryCode = countryCode;
    }

    public String getCountryName() {
        return countryName;
    }

    public void setCountryName(String countryName) {
        this.countryName = countryName;
    }
}
Gender.java
package org.o7planning.sbformvalidation.model;

public class Gender {

    public static final String MALE = "M";
    public static final String FEMALE = "F";
}
Des classes DAO (Data Access Object) servent à manipuler avec des ressources de données, telles que query, insert, update, delete. Ces classes sont habituellement annotées (annotate) par @Repository pourque Spring les gère comme des Spring BEAN.
AppUserDAO.java
package org.o7planning.sbformvalidation.dao;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.o7planning.sbformvalidation.formbean.AppUserForm;
import org.o7planning.sbformvalidation.model.AppUser;
import org.o7planning.sbformvalidation.model.Gender;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Repository;

@Repository
public class AppUserDAO {

    // Config in WebSecurityConfig
    @Autowired
    private PasswordEncoder passwordEncoder;

    private static final Map<Long, AppUser> USERS_MAP = new HashMap<>();

    static {
        initDATA();
    }

    private static void initDATA() {
        String encrytedPassword = "";

        AppUser tom = new AppUser(1L, "tom", "Tom", "Tom", //
                true, Gender.MALE, "tom@waltdisney.com", encrytedPassword, "US");

        AppUser jerry = new AppUser(2L, "jerry", "Jerry", "Jerry", //
                true, Gender.MALE, "jerry@waltdisney.com", encrytedPassword, "US");

        USERS_MAP.put(tom.getUserId(), tom);
        USERS_MAP.put(jerry.getUserId(), jerry);
    }

    public Long getMaxUserId() {
        long max = 0;
        for (Long id : USERS_MAP.keySet()) {
            if (id > max) {
                max = id;
            }
        }
        return max;
    }

    //

    public AppUser findAppUserByUserName(String userName) {
        Collection<AppUser> appUsers = USERS_MAP.values();
        for (AppUser u : appUsers) {
            if (u.getUserName().equals(userName)) {
                return u;
            }
        }
        return null;
    }

    public AppUser findAppUserByEmail(String email) {
        Collection<AppUser> appUsers = USERS_MAP.values();
        for (AppUser u : appUsers) {
            if (u.getEmail().equals(email)) {
                return u;
            }
        }
        return null;
    }

    public List<AppUser> getAppUsers() {
        List<AppUser> list = new ArrayList<>();

        list.addAll(USERS_MAP.values());
        return list;
    }

    public AppUser createAppUser(AppUserForm form) {
        Long userId = this.getMaxUserId() + 1;
        String encrytedPassword = this.passwordEncoder.encode(form.getPassword());

        AppUser user = new AppUser(userId, form.getUserName(), //
                form.getFirstName(), form.getLastName(), false, //
                form.getGender(), form.getEmail(), form.getCountryCode(), //
                encrytedPassword);

        USERS_MAP.put(userId, user);
        return user;
    }

}
CountryDAO.java
package org.o7planning.sbformvalidation.dao;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.o7planning.sbformvalidation.model.Country;
import org.springframework.stereotype.Repository;

@Repository
public class CountryDAO {

    private static final Map<String, Country> COUNTRIES_MAP = new HashMap<>();

    static {
        initDATA();
    }

    private static void initDATA() {

        Country vn = new Country("VN", "Vietnam");
        Country en = new Country("EN", "England");
        Country fr = new Country("FR", "France");
        Country us = new Country("US", "US");
        Country ru = new Country("RU", "Russia");

        COUNTRIES_MAP.put(vn.getCountryCode(), vn);
        COUNTRIES_MAP.put(en.getCountryCode(), en);
        COUNTRIES_MAP.put(fr.getCountryCode(), fr);
        COUNTRIES_MAP.put(us.getCountryCode(), us);
        COUNTRIES_MAP.put(ru.getCountryCode(), ru);
    }

    public Country findCountryByCode(String countryCode) {
        return COUNTRIES_MAP.get(countryCode);
    }

    public List<Country> getCountries() {
        List<Country> list = new ArrayList<>();
        list.addAll(COUNTRIES_MAP.values());
        return list;
    }

}

6. Form Bean, Validator

La classe AppUserForm représente les données saisies dans le formulaire d'enregistrement par des utilisateurs.
AppUserForm.java
package org.o7planning.sbformvalidation.formbean;

public class AppUserForm {

    private Long userId;
    private String userName;
    private String firstName;
    private String lastName;
    private boolean enabled;
    private String gender;
    private String email;
    private String password;
    private String confirmPassword;
    private String countryCode;

    public AppUserForm() {

    }

    public AppUserForm(Long userId, String userName, //
            String firstName, String lastName, boolean enabled, //
            String gender, String email, String countryCode, //
            String password, String confirmPassword) {
        this.userId = userId;
        this.userName = userName;
        this.firstName = firstName;
        this.lastName = lastName;
        this.enabled = enabled;
        this.gender = gender;
        this.email = email;
        this.countryCode = countryCode;
        this.password = password;
        this.confirmPassword = confirmPassword;
    }

    public Long getUserId() {
        return userId;
    }

    public void setUserId(Long userId) {
        this.userId = userId;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public boolean isEnabled() {
        return enabled;
    }

    public void setEnabled(boolean enabled) {
        this.enabled = enabled;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getCountryCode() {
        return countryCode;
    }

    public void setCountryCode(String countryCode) {
        this.countryCode = countryCode;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getConfirmPassword() {
        return confirmPassword;
    }

    public void setConfirmPassword(String confirmPassword) {
        this.confirmPassword = confirmPassword;
    }
}
La classe AppUserValidator sert à valider (validate) les informations que des utilisateurs ont saisi dans le formulaire. Donc AppUserValidator va valider (validate) les valeurs des champs (field) de l'objet AppUserForm.
AppUserValidator.java
package org.o7planning.sbformvalidation.validator;

import org.apache.commons.validator.routines.EmailValidator;
import org.o7planning.sbformvalidation.dao.AppUserDAO;
import org.o7planning.sbformvalidation.formbean.AppUserForm;
import org.o7planning.sbformvalidation.model.AppUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.validation.Errors;
import org.springframework.validation.ValidationUtils;
import org.springframework.validation.Validator;

@Component
public class AppUserValidator implements Validator {

	// common-validator library.
	private EmailValidator emailValidator = EmailValidator.getInstance();

	@Autowired
	private AppUserDAO appUserDAO;

	// The classes are supported by this validator.
	@Override
	public boolean supports(Class<?> clazz) {
		return clazz == AppUserForm.class;
	}

	@Override
	public void validate(Object target, Errors errors) {
		AppUserForm appUserForm = (AppUserForm) target;

		// Check the fields of AppUserForm.
		ValidationUtils.rejectIfEmptyOrWhitespace(errors, "userName", "NotEmpty.appUserForm.userName");
		ValidationUtils.rejectIfEmptyOrWhitespace(errors, "firstName", "NotEmpty.appUserForm.firstName");
		ValidationUtils.rejectIfEmptyOrWhitespace(errors, "lastName", "NotEmpty.appUserForm.lastName");
		ValidationUtils.rejectIfEmptyOrWhitespace(errors, "email", "NotEmpty.appUserForm.email");
		ValidationUtils.rejectIfEmptyOrWhitespace(errors, "password", "NotEmpty.appUserForm.password");
		ValidationUtils.rejectIfEmptyOrWhitespace(errors, "confirmPassword", "NotEmpty.appUserForm.confirmPassword");
		ValidationUtils.rejectIfEmptyOrWhitespace(errors, "gender", "NotEmpty.appUserForm.gender");
		ValidationUtils.rejectIfEmptyOrWhitespace(errors, "countryCode", "NotEmpty.appUserForm.countryCode");

		if (!this.emailValidator.isValid(appUserForm.getEmail())) {
			// Invalid email.
			errors.rejectValue("email", "Pattern.appUserForm.email");
		} else if (appUserForm.getUserId() == null) {
			AppUser dbUser = appUserDAO.findAppUserByEmail(appUserForm.getEmail());
			if (dbUser != null) {
				// Email has been used by another account.
				errors.rejectValue("email", "Duplicate.appUserForm.email");
			}
		}

		if (!errors.hasFieldErrors("userName")) {
			AppUser dbUser = appUserDAO.findAppUserByUserName(appUserForm.getUserName());
			if (dbUser != null) {
				// Username is not available.
				errors.rejectValue("userName", "Duplicate.appUserForm.userName");
			}
		}

		if (!errors.hasErrors()) {
			if (!appUserForm.getConfirmPassword().equals(appUserForm.getPassword())) {
				errors.rejectValue("confirmPassword", "Match.appUserForm.confirmPassword");
			}
		}
	}

}

7. Controller

MainController.java
package org.o7planning.sbformvalidation.controller;

import java.util.List;

import org.o7planning.sbformvalidation.dao.AppUserDAO;
import org.o7planning.sbformvalidation.dao.CountryDAO;
import org.o7planning.sbformvalidation.formbean.AppUserForm;
import org.o7planning.sbformvalidation.model.AppUser;
import org.o7planning.sbformvalidation.model.Country;
import org.o7planning.sbformvalidation.validator.AppUserValidator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
// import org.springframework.transaction.annotation.Transactional;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

@Controller
public class MainController {

   @Autowired
   private AppUserDAO appUserDAO;

   @Autowired
   private CountryDAO countryDAO;

   @Autowired
   private AppUserValidator appUserValidator;

   // Set a form validator
   @InitBinder
   protected void initBinder(WebDataBinder dataBinder) {
      // Form target
      Object target = dataBinder.getTarget();
      if (target == null) {
         return;
      }
      System.out.println("Target=" + target);

      if (target.getClass() == AppUserForm.class) {
         dataBinder.setValidator(appUserValidator);
      }
      // ...
   }

   @RequestMapping("/")
   public String viewHome(Model model) {

      return "welcomePage";
   }

   @RequestMapping("/members")
   public String viewMembers(Model model) {

      List<AppUser> list = appUserDAO.getAppUsers();

      model.addAttribute("members", list);

      return "membersPage";
   }

   @RequestMapping("/registerSuccessful")
   public String viewRegisterSuccessful(Model model) {

      return "registerSuccessfulPage";
   }

   // Show Register page.
   @RequestMapping(value = "/register", method = RequestMethod.GET)
   public String viewRegister(Model model) {

      AppUserForm form = new AppUserForm();
      List<Country> countries = countryDAO.getCountries();

      model.addAttribute("appUserForm", form);
      model.addAttribute("countries", countries);

      return "registerPage";
   }

   // This method is called to save the registration information.
   // @Validated: To ensure that this Form
   // has been Validated before this method is invoked.
   @RequestMapping(value = "/register", method = RequestMethod.POST)
   public String saveRegister(Model model, //
         @ModelAttribute("appUserForm") @Validated AppUserForm appUserForm, //
         BindingResult result, //
         final RedirectAttributes redirectAttributes) {

      // Validate result
      if (result.hasErrors()) {
         List<Country> countries = countryDAO.getCountries();
         model.addAttribute("countries", countries);
         return "registerPage";
      }
      AppUser newUser= null;
      try {
         newUser = appUserDAO.createAppUser(appUserForm);
      }
      // Other error!!
      catch (Exception e) {
         List<Country> countries = countryDAO.getCountries();
         model.addAttribute("countries", countries);
         model.addAttribute("errorMessage", "Error: " + e.getMessage());
         return "registerPage";
      }

      redirectAttributes.addFlashAttribute("flashUser", newUser);
      
      return "redirect:/registerSuccessful";
   }

}

8. Thymeleaf Template

_menu.html
<div xmlns:th="http://www.thymeleaf.org"
     style="border: 1px solid #ccc;padding:5px;margin-bottom:20px;">

  <a th:href="@{/}">Home</a>

     | &nbsp;

   <a th:href="@{/members}">Members</a>

     | &nbsp;

   <a th:href="@{/register}">Register</a>
  

</div>
membersPage.html
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
   <head>
      <title th:utext="${title}"></title>
   </head>
   
   <style>
      table th, table td {
         padding: 5px;
      }
      .message {
         color: blue;
      }
   </style>
   
   <body>
   
      <!-- Include _menu.html -->
      <th:block th:include="/_menu"></th:block>  
      
      <h2>Members</h2>
      
   
      
      <table border="1">
            <tr>
               <th>User Name</th>
               <th>First Name</th>
               <th>Last Name</th>
               <th>Email</th>
               <th>Gender</th>
            </tr>
            <tr th:each ="member : ${members}">
               <td th:utext="${member.userName}">...</td>
               <td th:utext="${member.firstName}">...</td>
               <td th:utext="${member.lastName}">...</td>
               <td th:utext="${member.email}">...</td>
               <td th:utext="${member.gender}">...</td>
            </tr>
      </table>
      
   </body>
</html>
registerPage.html
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
   <head>
      <title th:utext="${title}"></title>
      <style>
         th, td {
         padding: 5px;
         }
         td span  {
         font-size:90%;
         font-style: italic;
         color: red;
         }
         .error {
         color: red;
         font-style: italic;
         }
      </style>
   </head>
   <body>
      <!-- Include _menu.html -->
      <th:block th:include="/_menu"></th:block>
      
      <h2>Register</h2>
      
      <div th:if="${errorMessage != null}"
           th:utext="${errorMessage}" class="error">...</div>
      
      <form th:action="@{/register}" th:object="${appUserForm}" method="POST">
         <table>
            <tr>
               <td>User Name</td>
               <td><input type="text" th:field="*{userName}" /></td>
               <td>
                  <span th:if="${#fields.hasErrors('userName')}" th:errors="*{userName}">..</span>
               </td>
            </tr>
            <tr>
               <td>Password</td>
               <td><input type="password" th:field="*{password}" /> </td>
               <td>
                  <span th:if="${#fields.hasErrors('password')}" th:errors="*{password}">..</span>
               </td>
            </tr>
            <tr>
               <td>Confirm</td>
               <td><input type="password" th:field="*{confirmPassword}" /> </td>
               <td>
                  <span th:if="${#fields.hasErrors('confirmPassword')}" th:errors="*{confirmPassword}">..</span>
               </td>
            </tr>
            <tr>
               <td>Email</td>
               <td><input type="text" th:field="*{email}" /> </td>
               <td>
                  <span th:if="${#fields.hasErrors('email')}" th:errors="*{email}">..</span>
               </td>
            </tr>
            <tr>
               <td>First Name</td>
               <td><input type="text" th:field="*{firstName}" /> </td>
               <td>
                  <span th:if="${#fields.hasErrors('firstName')}" th:errors="*{firstName}">..</span>
               </td>
            </tr>
            <tr>
               <td>Last Name</td>
               <td><input type="text" th:field="*{lastName}" /> </td>
               <td>
                  <span th:if="${#fields.hasErrors('lastName')}" th:errors="*{lastName}">..</span>
               </td>
            </tr>
            <tr>
               <td>Gender</td>
               <td>
                  <select th:field="*{gender}">
                     <option value=""> -- </option>
                     <option value="M">Male</option>
                     <option value="F">Female</option>
                  </select>
               </td>
               <td>
                  <span th:if="${#fields.hasErrors('gender')}" th:errors="*{gender}">..</span>
               </td>
            </tr>
            <tr>
               <td>Country</td>
               <td>
                  <select th:field="*{countryCode}">
                     <option value=""> -- </option>
                     <option th:each="country : ${countries}"
                        th:value="${country.countryCode}"
                        th:utext="${country.countryName}"/>
                  </select>
               <td><span th:if="${#fields.hasErrors('countryCode')}" th:errors="*{countryCode}">..</span></td>
            </tr>
            <tr>
               <td>&nbsp;</td>
               <td>
                  <input type="submit" value="Submit" />
                  <a th:href="@{/}">Cancel</a>
               </td>
               <td>&nbsp;</td>
            </tr>
         </table>
      </form>
      
   </body>
</html>
registerSuccessfulPage.html
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
   <head>
      <title>Successfully registered</title>
      <style>
        span {color: blue;}
      </style>
   </head>
   
   <body>
   
      <!-- Include _menu.html -->
      <th:block th:include="/_menu"></th:block>  
      
      <h2>You have successfully registered!</h2>
      
      <div th:if="${flashUser != null}">
         <ul>
            <li>User Name: <span th:utext="${flashUser.userName}">..</span></li>
            <li>Email: <span th:utext="${flashUser.email}">..</span></li>
         </ul>
      </div>
     
      
   </body>
</html>
welcomePage.html
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
   <head>
      <title th:utext="${title}"></title>
   </head>
   
   <body>
   
      <!-- Include _menu.html -->
      <th:block th:include="/_menu"></th:block>  
      
      <h2>Home Page!</h2>
      
   </body>
</html>

9. Exécuter l'application

Cliquez sur le bouton droit du projet et sélectionnez :
  • Run As/Spring Boot App

Tutoriels Spring Boot

Show More