Input data validation is a critical piece of functionality in any web application because we cannot always rely on users to enter valid data. We need to safeguard our data-stores and business processes against both malformed as well as malicious data.
Based on my experience with Spring, I thought of writing a series of tutorials on different methods of validating form input when working with Spring MVC.
This is the first part of that series, and I will be focusing on basic form data (i.e. data submitted by the user) validation in Spring MVC 3 using declarative annotations. In the future, I am hoping to touch on more advanced topics such as:
- Displaying custom messages for validation errors
- Writing custom annotations to validate view models (or fields)
- Using JSR 303 groups to perform conditional validations
- Validation using Spring’s Validator interface
- Comparison of different validation methods in Spring MVC (pros, cons and when to use them)
Hopefully by the end of the series, you will have a good idea about Spring validation mechanisms, and be able to pick the best option(s) for a particular problem.
Continue on to learn how to perform basic annotation-based validations in your Spring MVC 3.x web applications.
Pre-requisits
Before proceeding ahead, I assume that you have some basic understanding about Spring MVC. Also, I would recommend installing Maven to build your projects if you fetch the sample code from GitHub.
Problem Description
Take a simple scenario where we are entering personal information in a web site, as shown in Figure 1. We want to ensure that we only accept the data if all of the following conditions are satisfied; otherwise we present the data back to the user with some helpful information to rectify the issues.
- All fields are mandatory except for ‘comments’.
- Age must be greater than (or equal to)
10
- Date of birth must be entered as ‘yyyy-MM-dd‘ format (e.g.
2000-10-22
) and must be in the past (i.e. can’t enter a date in future) - Comments can be at most
40
characters.
Step-by-step guide to implementing annotation-based validation in Spring MVC
-
Enable annotations
Declarative validations are specified as annotations (e.g. on field, method or class level). Therefore, we need to enable annotations in Spring in order for Spring MVC framework to pickup our declarative validations and apply them automatically when a form is submitted.
We do this by adding the
tag inside ourservlet context xml
file (typically underWEB-INF\spring\appServlet
folder)<!-- inside servlet-context.xml --> <mvc:annotation-driven /> <!-- rest omitted for brevity -->
-
Additional Project Dependencies
We are relying mainly on JSR 303 javax.validation API to add declarative validation support to our Beans. In addition, I also tend to use the
hibernate validator API
s that provide more enhanced constraints. If you are using Maven, you will need to add the following dependencies to your project:validation-api jar
hibernate-validator jar
Your
pom.xml
would contain the following dependencies (when using Maven):<dependency> <groupId>javax.validation</groupId> <artifactId>validation-api</artifactId> <version>1.0.0.GA</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-validator</artifactId> <version>4.1.0.Final</version> </dependency>
-
Add validation rules to the Bean
Before progressing further, let us have a look at our Bean object that handles the form submission:
/** * * Simple form bean without any validations */ public class SimpleFormBean { private String firstName; private String lastName; private int age; private Date dateOfBirth; private String comments; // getters and setters omitted for brevity }
This is just a plain old Java Object/Bean (POJO). Let us now define the constraints we described earlier using annotations from javax.validation, hibernate.validator and springframework.format.annotation packages.
import javax.validation.constraints.Min; import javax.validation.constraints.Past; import org.hibernate.validator.constraints.Length; import org.hibernate.validator.constraints.NotBlank; import org.hibernate.validator.constraints.NotEmpty; import org.springframework.format.annotation.DateTimeFormat; import org.springframework.format.annotation.DateTimeFormat.ISO; /** * Simple form bean with declarative validation rules * (using JSR 303 annotations) */ public class SimpleFormBean { // must be at least of length 1 @NotEmpty private String firstName; // is not blank (i.e. doesn't accept " ") @NotBlank private String lastName; // defines the minimum age to be 10 @Min(10) private int age; // ISO.DATE format is defined as 'yyyy-MM-dd' // e.g. 2012-02-16 @DateTimeFormat(iso = ISO.DATE) @Past private Date dateOfBirth; // at most 40 characters. Accepts null/empty @Length(max = 40) private String comments; // getters and setters omitted for brevity
Have a look at the Javax Validation API and Hibernate Validator documentation for more details on what other annotations are available.
-
@Valid annotation on the request handler
Next, we need to update our
Controller
, so that Spring framework will automatically perform Bean validation when the form is submitted. This happens before it reaches ourController
‘s handler method, which allows us to do special things if there are any validation errors (such as redirecting to a generic error page, or resetting form fields etc.).The code for the
Controller
shows the method that processes form submission.@Controller public class SimpleFormValidationController { @RequestMapping(value = "/simpleValidationExample", method = RequestMethod.POST) public ModelAndView processFormSubmissionWithValidation(@Valid SimpleFormBean formBean, BindingResult result) { ModelAndView mv = new ModelAndView(ViewNameConstants.VIEW_SIMPLE_FORM_VALIDATION); if (result.hasErrors()) { mv.addObject("message", "Some message you add on error"); mv.addObject("errors", result.getAllErrors()); return mv; } // Do any other normal processing you might do when the user input is valid mv.addObject("message", "Some message you add if everything is ok"); return mv; } }
There are a couple of important things happening in the above code snippet:
- Line #5: Mark the Bean representing the form data (
SimpleFormBean
) with@Valid
annotation. Spring framework will pick up this annotation as part of its request handling pipeline, and apply all the JSR 303 annotations on that object to validate the bean. We, as developers, do not have to worry about anything other than specifying the validation rules via annotations! - Line #9: The
org.springframework.validation.BindingResult
object contains the results of Spring validation. In this particular case, I am checking to see if there are any validation errors, so I can add some special message to be displayed when the page is refreshed. See the Spring API for more details.
If you feel adventurous, write a handler similar to above, but without the@Valid
annotation. You will see that your Bean is no longer validated by Spring despite your Bean having all the annotations! - Line #5: Mark the Bean representing the form data (
-
Re-displaying form with error messages
It is all good to validate your Bean, but it would be better if you can tell the user what he/she did wrong so their mistakes can be corrected.
Figure 2 shows the errors you get when the user enters incorrect information. Note that it doesn’t complain about ‘Comments’ field because it can accept null/empty values.
We can use the
form:errors
tag to display field-specific validation errors to the user. For example, the snippet below displays the error if the user submits an empty ‘firstName’ field.<form:errors path="firstName" cssClass="error" />
The full code snippet for generating the form is shown below:
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <%@ taglib uri="http://www.springframework.org/tags/form" prefix="form" %> ... <form:form id="form" method="post" modelAttribute="simpleFormBean"> <div id="formContent"> <div class="header"> <h2>Form</h2> <c:if test="${not empty message}"> <div id="message" class="success">${message}</div> </c:if> <s:bind path="*"> <c:if test="${status.error}"> <div id="message" class="error">Form has errors</div> </c:if> </s:bind> </div> <fieldset> <legend>Personal Info</legend> <form:label path="firstName">First Name <form:errors path="firstName" cssClass="error" /></form:label> <form:input path="firstName" /> <form:label path="lastName">Last Name <form:errors path="lastName" cssClass="error" /></form:label> <form:input path="lastName" /> <form:label path="age">Age <form:errors path="age" cssClass="error" /></form:label> <form:input path="age" /> <form:label path="dateOfBirth"> Date of Birth (in form yyyy-mm-dd) <form:errors path="dateOfBirth" cssClass="error" /> </form:label> <form:input path="dateOfBirth" /> <form:label path="comments">Comments (40 letters max) <form:errors path="comments" cssClass="error" /></form:label> <form:input path="comments" /> </fieldset> <p><button type="submit">Submit (with validations)</button></p> </div> </form:form>
Things of interest about the above html snippet include:
- Use of Spring form taglib to construct the form, labels and input fields. This enables Spring to bind our Bean object to the form data.
- Use of
form:errors
attribute to display errors, if there are any. This is a special attribute that is part of the Spring tag library that displays validation errors based on the field that is wrong.
-
Customising validation error messages
The messages that you see in red – in Figure 2 above – are default error messages generated by the validators we used. As you can see some of the validator error messages are straightforward, but others are way too verbose to be displayed to an end-user (e.g. error message for date validation above).
The final part is to customise the error messages displayed. There are a couple of ways in which you can modify the validation messages.
- Specifying the custom message when you define the constraint – This is the simplest, and possibly the least elegant of solutions. The code snippet below show us customising the error message when the user doesn’t enter any value for the ‘First name’ field.
public class SimpleFormBean { @NotEmpty(message = "Hi, first name cannot be empty. Please enter something here :)") private String firstName; // ... omitted for brevity }
Now, instead of “
may not be empty
” (as shown in Figure 2 above), you will see “Hi, first name cannot be empty. Please enter something here :)
” as the error message.The reason why I said this approach is not elegant is that you cannot display locale specific messages easily with this approach. This where the 2nd solution for this problem comes in handy!
- Use a properties file to manage messages and customise the message key – In short, we define our messages in a
.properties
file (e.g.webapps\resources\messages.properties
) as key-value pairs. Instead of the actual message, we define the key as part of the message attribute for the annotation. The code below illustrates how we can display the same custom message as above using this approach:## messages.properties file NotEmpty.SimpleFormBean.FirstNameField = Hi, first name cannot be empty. Please enter something here :)
And our Bean would look like:
public class SimpleFormBean { @NotEmpty(message = "NotEmpty.SimpleFormBean.FirstNameField") private String firstName; // ... omitted for brevity }
We need to do bit more configuration to Spring in order to allow Spring to find these messages. Rather than deviating from the main topic, I will write another article soon describing how to achieve this.
- Specifying the custom message when you define the constraint – This is the simplest, and possibly the least elegant of solutions. The code snippet below show us customising the error message when the user doesn’t enter any value for the ‘First name’ field.
Conclusions
In this tutorial I’ve covered the following aspects related to validating form attributes/data (also known as form backing objects) using JSR 303 compliant declarative annotations in Spring MVC.
- Enabling annotations in Spring MVC
- Using @Valid to enable Spring validation on form objects
- Defining validations on Beans using JSR 303 compliant annotations
- Writing jsp/html to display validation errors on screen
- Customising default validation messages
Hope you found the article informative, and let me know if you want me to clarify anything.
A fully working sample project can be found at GitHub if you are interested in playing around.
Thanks!
References
- Source code for GitHub project at :
- Spring Validation Documentation – for more in-depth details about Spring validation.