Form Submitting with Spring Boot Validation
Tags: java, springframework, thymeleaf
Published: 2020-07-20 18:29:05

#Introduction
This tutorial is how to create and submit a form and how to validate our registration form. 

#Necessary Environments
- IDE - Eclipse 

- JDK 1.8 or later

- Gradle 4+ or Maven 3.2+

- [Spring Tool Suite (STS)](https://dev.to/zarlihninn/spring-boot-hello-world-example-by-using-sts-4nn) or you can use [Spring Initilizr](https://dev.to/reytech-lesson/spring-boot-hello-world-example-by-using-spring-initilizr-b7o)

#Source Code
You can refer to the source code below.
https://github.com/ZarLiHninn/Form-Submitting-with-Spring-Boot-Validation

#Let’s start by following these steps
##Step 1. Creating a project
- File → New → Spring Starter Project
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/0t6hdwpntdtcp0alejt9.png)
- Fill the necessary information and add dependencies.
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/x6jbvd5cenp6s3yqb9zf.png)
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/mxdy3tzm2ptkle2xw2af.png)
- Project Explorer is like this.
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/nd07wmfytnjvfidlav7g.png)

##Step 2. Let’s start code
###Student.java
```java
package com.reytech.demo.model;

import java.time.LocalDate;
import java.util.List;
import javax.validation.constraints.Email;
import javax.validation.constraints.Max;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Past;
import javax.validation.constraints.Positive;
import javax.validation.constraints.Size;
import org.springframework.format.annotation.DateTimeFormat;

public class Student {

    @NotEmpty(message = "{validation.name.NotEmpty}")
    @Size(min = 2, max = 50, message = "{validation.name.Size}")
    private String name;

    @NotNull(message = "{validation.age.NotNull}")
    @Positive(message = "{validation.age.Positive}")
    @Max(value = 18, message = "{validation.age.Maximum}")
    private Integer age;

    @NotEmpty(message = "{validation.email.NotEmpty}")
    @Email(message = "{validation.email.Type}")
    private String email;

    @NotEmpty(message = "{validation.subjects.NotEmpty}")
    private List <String> subjects;

    @NotNull(message = "{validation.birthDate.NotNull}")
    @Past(message = "{validation.birthDate.Past}")
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private LocalDate birthDate;

    @NotEmpty(message = "{validation.gender.NotEmpty}")
    private String gender;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getEmail() {
        return email;
    }

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

    public LocalDate getBirthDate() {
        return birthDate;
    }

    public void setBirthDate(LocalDate birthDate) {
        this.birthDate = birthDate;
    }

    public String getGender() {
        return gender;
    }

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

    public List <String> getSubjects() {
        return subjects;
    }

    public void setSubjects(List <String> subjects) {
        this.subjects = subjects;
    }

}
```
- We will store student information in Student.class model.

- And, @Size, @NotEmpty, @Email, etc. are to validate input when the user fills an error input.

- We will use custom message by creating message.properties. (e.g.message=”{validation.name.NotEmpty}”, you can see the custom message in messages.properties file.)

###messages.properties
- We will set custom messages.
```properties
validation.name.NotEmpty=Please fill in Name
validation.name.Size=Name size must be between 2 and 30
validation.name.Pattern=Name must be only characters
validation.age.NotNull=Please fill in Age
validation.age.Maximum=Age must be under 18
validation.age.Positive=Age must not be negative value and 0
validation.age.Pattern=Age must be only numbers
validation.email.NotEmpty=Please fill in Email
validation.email.Type=Please fill in valid Email
validation.birthDate.NotNull=Please fill in Birth Date
validation.birthDate.Past=Please fill in valid Birth Date
validation.subjects.NotEmpty=Please select Subjects
validation.gender.NotEmpty=Please select Gender
```

###AppConfiguration.java
- To handle message.properties, MessageSource and LocalValidatorFactoryBean are needed.
```java
package com.reytech.demo;

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.validation.beanvalidation.LocalValidatorFactoryBean;

@Configuration
public class AppConfiguration {
	
    @Bean
    public MessageSource messageSource() {
        ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
        messageSource.setBasename("classpath:messages");
        messageSource.setDefaultEncoding("UTF-8");
        return messageSource;
    }

    @Bean
    public LocalValidatorFactoryBean validator() {
        LocalValidatorFactoryBean bean = new LocalValidatorFactoryBean();
        bean.setValidationMessageSource(messageSource());
        return bean;
    }
}
```

###Gender.java
```java
package com.reytech.demo.constant;

public enum Gender {
   MALE,FEMALE,OTHER
}
```

###Subject.java
```java
package com.reytech.demo.constant;

public enum Subject {
	MYANMAR("Myanmar"),
	ENGLISH("English"),
	MATH("Math"),
	CHEMISTRY("Chemistry"),
	PHYSICS("Physics"),
	BIO("Bio"),
	ECO("Eco");

	private final String value;

	private Subject(String value) {
		this.value = value;
	}

	public String getValue() {
		return value;
	}
}
```
###HomeController.java
```java
package com.reytech.demo.controller;

import javax.validation.Valid;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import com.reytech.demo.constant.Gender;
import com.reytech.demo.constant.Subject;
import com.reytech.demo.model.Student;

@Controller
public class HomeController {

	@GetMapping("/form")
	public String showForm(Model model) {
		Student student = new Student();
		model.addAttribute("student", student);
		model.addAttribute("gender_value", Gender.values());
		model.addAttribute("subject_value", Subject.values());
		return "form";
	}

	@PostMapping("/form/result")
	public String submitForm(@Valid Student student, BindingResult bindingResult, Model model) {
		if (bindingResult.hasErrors()) {
			model.addAttribute("student", student);
			model.addAttribute("gender_value", Gender.values());
			model.addAttribute("subject_value", Subject.values());
			return "form";
		} else {
			model.addAttribute("result", student);
			return "result";
		}
    }

	@GetMapping("/back")
	public String backHome() {
		return "redirect:/form";
	}

}
```
The controller is used to handle GET/POST requests at /HTTP endpoint.

- The showForm() method is to reach localhost:/8080/form as a GET request and return the view which is form.html by using thymeleaf. It uses a Model object to expose a new Student object to the view template. The values of Gender and Subjects are carried by using the model.addAttribute and displayed the result in result.html.

- The saveForm() method is to reach localhost:8080/form/result as a POST request when submitting data by the user. The submitted data is saved in “result” and carried by model.addAttribute.  And this method returns the view which is result.html but it returns back to form.html when submitted data are errors.

###form.html
- We will create a form to fill data in form.html.
```html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
   <head>
      <meta charset="UTF-8" />
      <title>Form_Registration</title>
      <link rel="stylesheet"
         href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
      <link rel="stylesheet"
         href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css">
      <script src="https://code.jquery.com/jquery-1.12.4.js"></script>
      <script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
      <script
         src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js"></script>
      <script>
         $(function() {
             $("#datepicker").datepicker({dateFormat:"yy-mm-dd"});
         });
      </script>
      <style>
         .error{
         color:red;
         }
      </style>
   </head>
   <body>
      <div class="container">
         <h1>Form Registration</h1>
         <form th:action="@{/form/result}" th:object="${student}" method="post">
            <div class="form-group">
               <label>Student Name</label> 
               <input class="form-control" type="text" th:field="*{name}" />
               <span class="error" th:if="${#fields.hasErrors('name')}" th:errors="*{name}">Name Error</span>
            </div>
            <div class="form-group">
               <label>Age</label> 
               <input class="form-control" type="text" th:field="*{age}" />
               <span class="error" th:if="${#fields.hasErrors('age')}" th:errors="*{age}">Age Error</span>
            </div>
            <div class="form-group">
               <label>Email</label> 
               <input class="form-control" type="text" th:field="*{email}" />
               <span class="error" th:if="${#fields.hasErrors('email')}" th:errors="*{email}">Email Error</span>
            </div>
            <div class="form-group">
               <label>BirthDate</label> 
               <input class="form-control" type="text" id="datepicker" th:field="*{birthDate}">
               <span class="error" th:if="${#fields.hasErrors('birthDate')}" th:errors="*{birthDate}">BirthDate Error</span>
            </div>
            <div>
               <p>Subjects</p>
               <span th:each="sub: ${subject_value}"> 
	               <input type="checkbox" th:field="*{subjects}" th:value="${sub}" /> 
	               <label th:text="${sub}">Subjects</label>
               </span>
               <p class="error" th:if="${#fields.hasErrors('subjects')}" th:errors="*{subjects}">Subject Error</p>
            </div>
            <div>
               <p>Gender</p>
               <span th:each="gen: ${gender_value}"> 
	               <input type="radio" th:field="*{gender}" th:value="${gen}" /> 
	               <label th:text="${gen}">Gender</label>
               </span>
               <p class="error" th:if="${#fields.hasErrors('gender')}" th:errors="*{gender}">Gender Error</p>
            </div>
            <input type="submit" value="Send" />
         </form>
      </div>
   </body>
</html>
```

###result.html
- We will create a page to show submitted data in result.html.
```html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
   <head>
      <meta charset="UTF-8" />
      <title>Result</title>
      <link rel="stylesheet"
         href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
      <link rel="stylesheet"
         href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css">
      <script src="https://code.jquery.com/jquery-1.12.4.js"></script>
      <script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
      <script
         src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js"></script>
   </head>
   <body>
      <div th:if="${result != null}" class="container">
         <h1>Result</h1>
         <table class="table table-bordered">
            <tr>
               <th>Title</th>
               <th>Value</th>
            </tr>
            <tr>
               <td>Student Name</td>
               <td><span th:text="${result.name}"></span></td>
            </tr>
            <tr>
               <td>Age</td>
               <td><span th:text="${result.age}"></span></td>
            </tr>
            <tr>
               <td>Email</td>
               <td><span th:text="${result.email}"></span></td>
            </tr>
            <tr>
               <td>Date of Birth</td>
               <td><span th:text="${result.birthDate}"></span></td>
            </tr>
            <tr>
               <td>Subjects</td>
               <td>
                  <ul>
                     <li th:each="sub : ${result.subjects}" th:text="${sub}"></li>
                  </ul>
               </td>
            </tr>
            <tr>
               <td>Gender</td>
               <td><span th:text="${result.gender}"></span></td>
            </tr>
         </table>
      </div>
      <div class="container">
         <a th:href="@{/back}">Back</a>
      </div>
   </body>
</html>
```
If you need more information about Thymeleaf templates, take a look at [Thymeleaf Tutorial](https://www.thymeleaf.org/doc/tutorials/2.1/thymeleafspring.html#creating-a-form).

##Step 3. Let’s run our application
1. Let’s go to localhost:8080/form and we will see the following form.
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/ucrqik3q1jnk42ug06ja.png)

2. Let’s submit some error data and then we can see error messages.
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/2bv0e3lfn7sxxal0jsa8.png)

3. Let’s submit the right data.
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/m0b2y906jutq0670ff9o.png)

4. Now we can see the result in localhost:8080/form/result.
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/0l1jk4vh2tvx3zks4esm.png)