Quantcast
Channel: Thiranjith's Blog
Viewing all articles
Browse latest Browse all 11

Spring MVC Bean Validation with Annotations

$
0
0

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:

  1. Displaying custom messages for validation errors
  2. Writing custom annotations to validate view models (or fields)
  3. Using JSR 303 groups to perform conditional validations
  4. Validation using Spring’s Validator interface
  5. 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.

Figure 1: Form to enter personal information

  • 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

  1. 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 our servlet context xml file (typically under WEB-INF\spring\appServlet folder)

    <!-- inside servlet-context.xml -->
    <mvc:annotation-driven />
    
    <!-- rest omitted for brevity -->

  2. 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 APIs 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>

  3. 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.

  4. @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 our Controller‘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:

    1. 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!
    2. 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!

  5. 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: After submitting the form with errors

    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:

    1. 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.
    2. 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.
  6. 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.

    1. 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!

    2. 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.

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.

  1. Enabling annotations in Spring MVC
  2. Using @Valid to enable Spring validation on form objects
  3. Defining validations on Beans using JSR 303 compliant annotations
  4. Writing jsp/html to display validation errors on screen
  5. 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


Viewing all articles
Browse latest Browse all 11

Latest Images

Trending Articles





Latest Images