If you’ve got a web app that requires users to fill out a form, then you’ll need to validate that input. Otherwise, you run the risk of persisting inaccurate or incomplete data.

Fortunately, it’s a breeze to implement form field validation with Spring Boot and Thymeleaf.

In this article, we’ll go over how you can easily add validation to any form without jumping through JQuery hoops. In other words, we’ll look at server-side validation rather than client-side validation.

I have all the code for this tutorial on GitHub. You can just import that code into your own Eclipse workbench.

 

The Use Case

The use case for this tutorial is a Human Resources rep filling out a form for a new employee. The form will include fields for the employee’s first name, last name, Social Security number, email address, hours per week, and department.

Obviously, we want to make sure that accurate info is entered into each field. That’s where the validation comes into play.

 

The Model

We only need a single model for this tutorial: one that represents the employee. To that end, we’ll create an Employee class that’s a Java bean with some validation detail.

Here’s what the Employee fields look like:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Size(min=1, max=32, message="First name must be between 1 and 32 characters")
private String firstName;
 
@Size(min=1, max=32, message="Last name must be between 1 and 32 characters")
private String lastName;
 
@NotNull(message="Please enter an SSN")
@Pattern(regexp = "^[0-9][0-9]{2}-[0-9]{2}-[0-9]{4}$", message="SSN must use numbers in this format: XXX-YY-ZZZZ")
private String ssn;
 
@NotNull(message="Please enter a number of hours per week")
@Min(10)
@Max(40)
private Integer hoursPerWeek;
 
@NotNull
@Pattern(regexp = "^[_A-Za-z0-9-\\+]+(\\.[_A-Za-z0-9-]+)*@[A-Za-z0-9-]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})$", message="Email address is invalid")
private String emailAddress;
 
@NotNull
@Pattern(regexp="^(?=\\s*\\S).*$", message="Please select a department")
private String department;

As you can see, we’ve created a fields for all of the data that we’re going to collect on the form. That’s standard Java bean convention.

What’s not standard Java bean convention is the annotations you see above each field. Those will be used for validation purposes.

The @Size annotation that you see on some of the fields is used to dictate minimum and maximum length. For example, the first and last names must be between 1 and 32 characters in length.

So if a user enters a last name that’s 33 characters long, the validation will fail.

The “message” attribute added to some of the annotations is the message that the user will see on the front end if that validation fails. For example, a user who enters a last name that’s 33 characters in length will see a message that reads: “Last name must be between 1 and 32 characters.”

In addition to the @Size annotation, we’ve also included a @NotNull annotation on some fields. As the name implies, that means the field can’t be left blank.

The @Pattern annotation is used for regular expression (regex) pattern matching. We use that to ensure that the Social Security number and the email address the user enters adhere to standard conventions. For example, the email address should include an @ sign and a domain name with period in it (such as “johnsmith@xyz.com”).

The @Min and @Max annotations are used for numerical entries. As you can imagine, they’re used to specify a minimum and maximum value.

The Java bean also has the usual getters and setters as well.

 

The Controller Class

Now that we’ve completed the model, it’s time to set up a controller class. In this case, we’ll need a class with two methods: one that handles a GET request and one that handles a POST request.

The GET request will serve up an empty form when the user accesses the page to enter employee info.

The POST request will handle what happens when the user has completed the form and clicks on the “Submit” button. That’s where the validation will occur.

Here’s the method that handles the GET request:

1
2
3
4
5
6
7
8
9
10
11
@RequestMapping(value = "/form", method=RequestMethod.GET)
public String form(Model model) {
   //instantiate an employee object
   Employee employee = new Employee();
 
   //add it to the model
   model.addAttribute("employee", employee);
 
   //get out
   return "form";
}

As you can see, this method will respond to a user who accesses the /form path. We’ll instantiate an empty Employee object and stuff it in the model.

Finally, we’ll return “form,” which corresponds to form.html. In a typical Maven/Spring Boot structure, that file is found in the templates folder under src/main/resources.

form-file

Here’s the method that handles the POST request:

1
2
3
4
5
6
7
8
9
10
@RequestMapping(value = "/form", method=RequestMethod.POST)
public String formSubmit(@Valid Employee employee, BindingResult bindingResult, Model model) {
    //check for errors
    if (bindingResult.hasErrors()) {
       return "form";
    }
  
    //if there are no errors, show form success screen
    return "formSuccess";
}

That’s not too complicated, is it?

As you can see, we’re accepting an Employee object in the form submission process. In this case, the fields in the object will contain the values that the user entered on the form.

Pay particular attention to the @Valid attribute. That means we’re going to validate the data in the object.

Next, we pass in a BindingResult object. That’s the object that will inform us if there are errors in the validation. It’s important to place it right after the object that we’re validating in the method signature.

In the code itself, all we’re doing is grabbing the boolean returned by hasErrors() on the BindingResult object. If that returns true, then there are validation errors and we kick the user back to the form.

If there are no errors, we return “formSuccess.” The means the user will see the formSuccess.html file. For the purposes of this tutorial, that file is just a simple “Congratulations” page.

Our next step is to write code in form.html so that it supports validation.

 

Binding the Form to the Object

Fortunately, Thymeleaf makes it easy to associate a form with an object. In our example, we want to associate the form with the Employee object.

Recall that we instantiated an empty Employee object in the controller. So the form will be empty as well when the user arrives there for the first time.

Here’s how we associate a form with an object in Thymeleaf:

1
<form id="employeeForm" th:action="@{/form}" th:object="${employee}" method="POST">

Note the th:object attribute in the <form> tag. That points to the Employee object that we put in the model.

Of course, we also define the action for our form. In this case, it’s the same URL that brought us to the form in the first place: /form.

As you can see, though, we’re performing a POST instead of a GET when the user submits the form. That will run a different method in the controller class.

 

Binding Fields

Next, we need to bind specific fields on the form to fields in the Java bean.

For example, we have a firstName field in the Employee object. We need to associate that Java variable with a text field on the HTML form.

Fortunately, that’s also easy to do in Thymeleaf:

1
2
3
4
5
6
<label>First Name</label>
<div th:classappend="${#fields.hasErrors('firstName')} ? 'input-icon right' : ''">
 <i th:if="${#fields.hasErrors('firstName')}" class="fa fa-exclamation tooltips" data-original-title="please enter a valid first name" data-container="body"></i>
 <input type="text" class="form-control" maxlength="32" th:field="*{firstName}" placeholder="Between 1 and 32 characters"/>
 <span th:if="${#fields.hasErrors('firstName')}" class="help-block" th:errors="*{firstName}"></span>
</div>

There’s quite a bit going on in that block. Let’s break it down piece-by-piece.

The <div> element at the very top checks for a validation error on the firstName field. If there is an error, that it will add a couple of CSS classes to the element. Those classes will show a bright red exclamation point on the side of the field so that the user knows the field didn’t pass validation.

exclamation-point

Pay particular attention to this syntax in the code above: ${#fields.hasErrors(‘firstName’)}

That’s Thymeleaf-speak for checking if the specified field (“firstName”) has an error.

The next line in the code displays a tooltip if there’s an validation error. The tooltip reads: “please enter a valid first name.” The user will see it by hovering the mouse cursor over the exclamation point.

tooltip

The next line is the very important <input> tag. Pay careful attention to the th:field attribute. That’s where we bind the Java bean variable to the form field.

As you can see, the syntax looks like this: th:field=”*{firstName}”

Since we’ve already associated a Java bean (Employee) with the form, that line tells Thymeleaf to look for a variable called “firstName” in the object and associate that variable with the text field.

The <span> element at the end of the code block is where the error message will appear if the field fails to pass validation. Recall that we defined that error message in the bean itself:

1
2
@Size(min=1, max=32, message="First name must be between 1 and 32 characters")
private String firstName;

The “message” attribute is the message that will appear if the field fails the validation specified in the @Size annotation.

That’s basically it. Now, we’ll follow the same pattern for the rest of the fields that we used for the firstName field.

You can see the code for the entire page on GitHub.

 

Testing It Out

At this point, we’ve developed the Controller, the form bean, and the HTML pages. We’re ready to deploy and test it out!

Just use Maven to build the JAR file and run it as a Java application with the -jar option:

java -jar validationdemo-1.0.jar

As with most of my other demos, it runs on port 8090 (just in case you’re using the default port 8080 for something else). So you can access the app with the following URL:

http://localhost:8090/form

That should bring up the following screen:

empty-form

Just for kicks, click on the “Submit” button without filling out anything. The response you get should look like this:

failed-validation

As you can see, every field failed validation. That’s exactly what we expected.

Now, it’s time to experiment. Enter values in some fields and not others. Enter an unstructured SSN. Enter an email address without an @ sign.

In short: make sure all of the fields validate properly.

 

Wrapping It Up

If you’d like to view all of the code used in this tutorial, head on over to GitHub and have a look.

Have fun!