Spring 4 MVC - Form validation example using Validator interface

Spring 4 MVC - Form validation example using Validator interface

Technologies used:   Java SE 1.8 | Spring 4.3.7.RELEASE | Maven 3.3.9 | Apache Tomcat 7.0.47 | Eclipse Neon.3

In Spring MVC, Validator interface can be used to validate all fields of a form. We can configure it within a controller class through the @InitBinder annotation.

The @InitBinder annotation is method level annotation, used to initialize the WebDataBinder.  The WebDataBinder class binds the web request parameters to model objects on each http request.

We can register a custom Validtor within a controller class via the WebDataBinder.addValidators() method. When an argument or model object, annotated with @Validated annotation, of a handler method is encountered, it will be validated by registered custom validator.

If any validation violations have been detected, it will automatically be exposed as errors in the BindingResult, which is accessible as an argument in handler method.

Let write a simple Spring MVC application to demonstrate how to validate a form using the Validate interface.

 

Project structure

Review the following web project structure.

Spring 4 MVC - Form validation example | BORAJI.COM

Refer this article to learn - How to create a web project using maven build tool in eclipse IDE.

 

Jar dependencies

In pom.xml file of your maven project, add the dependencies below.

pom.xml

<dependencies>
   <!-- Spring MVC Dependency -->
   <dependency>
     <groupId>org.springframework</groupId>
     <artifactId>spring-webmvc</artifactId>
     <version>4.3.7.RELEASE</version>
   </dependency>
   
   <!-- JSTL Dependency -->
   <dependency>
     <groupId>javax.servlet.jsp.jstl</groupId>
     <artifactId>javax.servlet.jsp.jstl-api</artifactId>
     <version>1.2.1</version>
   </dependency>
   <dependency>
     <groupId>taglibs</groupId>
     <artifactId>standard</artifactId>
     <version>1.1.2</version>
   </dependency>
   
   <!-- Servlet Dependency -->
   <dependency>
     <groupId>javax.servlet</groupId>
     <artifactId>javax.servlet-api</artifactId>
     <version>3.1.0</version>
     <scope>provided</scope>
   </dependency>
   
   <!-- JSP Dependency -->
   <dependency>
     <groupId>javax.servlet.jsp</groupId>
     <artifactId>javax.servlet.jsp-api</artifactId>
     <version>2.3.1</version>
     <scope>provided</scope>
   </dependency>
</dependencies>

 

Model class

First, we will create a model class as follows. It will be used for binding from data to the model and exposing model data to views.

User.java

package com.boraji.tutorial.spring.model;

import java.util.List;

public class User {
   private String name;
   private String email;
   private String gender;
   private List<String> languages;

   //Setter and Getter methods

}

 

Custom Validator

Now create a customer validator by implementing the Validator interface to validate the User model as follows.

UserValidator.java

package com.boraji.tutorial.spring.validator;

import java.util.regex.Pattern;

import org.springframework.stereotype.Component;
import org.springframework.validation.Errors;
import org.springframework.validation.ValidationUtils;
import org.springframework.validation.Validator;

import com.boraji.tutorial.spring.model.User;

/**
 * @author imssbora
 */
@Component
public class UserValidator implements Validator {

   @Override
   public boolean supports(Class<?> clazz) {
      return User.class.equals(clazz);
   }

   @Override
   public void validate(Object obj, Errors err) {

      ValidationUtils.rejectIfEmpty(err, "name", "user.name.empty");
      ValidationUtils.rejectIfEmpty(err, "email", "user.email.empty");
      ValidationUtils.rejectIfEmpty(err, "gender", "user.gender.empty");
      ValidationUtils.rejectIfEmpty(err, "languages", "user.languages.empty");

      User user = (User) obj;

      Pattern pattern = Pattern.compile("^[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,6}$",
            Pattern.CASE_INSENSITIVE);
      if (!(pattern.matcher(user.getEmail()).matches())) {
         err.rejectValue("email", "user.email.invalid");
      }

   }

}

You can use the ValidationUtils helper class to check the null or empty fields of a model object. It provides the rejectIfEmpty() and rejectIfEmptyOrWhitespace() methods to reject the null or empty fields with a error code.

You can also use the Errors.rejectValue() method to reject any field of a model object with an error code.

We will define the error codes and their corresponding values in messages.properties file under src/main/resources folder as follows.

messages.properties

user.name.empty = Enter a valid name.
user.email.empty = Enter a valid email.
user.email.invalid = Invalid email! Please enter valid email.
user.gender.empty = Select gender.
user.languages.empty = Select at least one language.

 

Controller class

 Create a controller class and add the custom validator in it through the @InitBinder.

UserController.java

package com.boraji.tutorial.spring.controller;

import java.util.Locale;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
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.GetMapping;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;

import com.boraji.tutorial.spring.model.User;
import com.boraji.tutorial.spring.validator.UserValidator;

/**
 * @author imssbora
 */
@Controller
public class UserController {

   @Autowired
   private UserValidator userValidator;
   
   @InitBinder
   protected void initBinder(WebDataBinder binder) {
      binder.addValidators(userValidator);
   }

   @GetMapping("/")
   public String userForm(Locale locale,Model model) {
      model.addAttribute("user", new User());
      return "userForm";
   }

   /*
    * Save user object
    */
   @PostMapping("/saveUser")
   public String saveUser(@ModelAttribute("user") @Validated User user, BindingResult result,
         Model model) {

      if (result.hasErrors()) {
         return "userForm";
      }
      return "success";
   }
}

 

JSP Views

Create userForm.jsp file under src\main\webapp\WEB-INF\views folder to take user input.

userForm.jsp

<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
  pageEncoding="ISO-8859-1"%>
<[email protected] uri="http://www.springframework.org/tags/form" prefix="form"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>BORAJI.COM</title>
<style type="text/css">
.error {
	color: red;
}
</style>
</head>
<body>
  <h2>User Input From</h2>
  <hr />
  <form:form action="saveUser" method="post" modelAttribute="user">
    <table>
      <tr>
        <th>Name</th>
        <td>
          <form:input path="name" /> 
          <form:errors path="name" cssClass="error" />
         </td>
      </tr>
      <tr>
        <th>Email</th>
        <td>
            <form:input path="email" /> 
            <form:errors path="email" cssClass="error" />
         </td>
      </tr>
      <tr>
        <th>Gender</th>
        <td>
          <form:radiobutton path="gender" value="Male" label="Male" /> 
          <form:radiobutton path="gender" value="Female" label="Female" /> 
          <form:errors path="gender" cssClass="error" />
         </td>
      </tr>
      <tr>
        <th valign="top">Languages</th>
        <td>
          <form:select path="languages" multiple="true">
            <form:option value="US English">US English</form:option>
            <form:option value="UK English">UK English</form:option>
            <form:option value="Spanish">Spanish</form:option>
            <form:option value="Hindi">Hindi</form:option>
            <form:option value="Mandarin">Mandarin</form:option>
          </form:select>
          <form:errors path="languages" cssClass="error" />
         </td>
      </tr>
      <tr>
        <td><button type="submit">Submit</button></td>
      </tr>
    </table>
  </form:form>
</body>
</html>

The <form:errors> is used to display the error messages and the path attribute indicate the field name of model class for which the error message should be displayed.

Create another success.jsp file under src\main\webapp\WEB-INF\views folder to display the user information on successful validation.

success.jsp

<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
  pageEncoding="ISO-8859-1"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>BORAJI.COM</title>
</head>
<body>
  <h2>User Success From</h2>
  <hr />

  <table>
    <tr>
      <th>Name</th>
      <td>${user.name}</td>
    </tr>
    <tr>
      <th>Email</th>
      <td>${user.email}</td>
    </tr>
    <tr>
      <th>Gender</th>
      <td>${user.gender}</td>
    </tr>
    <tr>
      <th valign="top">Languages</th>
      <td>${user.languages}</td>
    </tr>
  </table>
</body>
</html>

 

Spring configuration

Create a web @Configuration class annotated with @EnableWebMvc and @ComponentScan as follows.

Also declare a MessageSource bean method to accesses resource bundles (i.e. messages.properties) using specified basenames.

WebConfig.java

package com.boraji.tutorial.spring.config;

import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ResourceBundleMessageSource;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import org.springframework.web.servlet.view.JstlView;

/**
 * @author imssbora
 */

@Configuration
@EnableWebMvc
@ComponentScan(basePackages = { "com.boraji.tutorial.spring.controller",
      "com.boraji.tutorial.spring.validator" })
public class WebConfig extends WebMvcConfigurerAdapter{

   @Bean
   public InternalResourceViewResolver resolver() {
      InternalResourceViewResolver resolver = new InternalResourceViewResolver();
      resolver.setViewClass(JstlView.class);
      resolver.setPrefix("/WEB-INF/views/");
      resolver.setSuffix(".jsp");
      return resolver;
   }

   @Bean
   public MessageSource messageSource() {
      ResourceBundleMessageSource source = new ResourceBundleMessageSource();
      source.setBasename("messages");
      return source;
   }
}

The @EnableWebMvc enables default Spring MVC configuration and provides the functionality equivalent to <mvc:annotation-driven/> element in XML based configuration.

The @ComponentScan scans the stereotype annotations (@Controller@Service etc...) in a package specified by basePackages attribute.

 

Servlet container initialization

Create a container initializer class by extending the AbstractAnnotationConfigDispatcherServletInitializer class as follows.

MyWebAppInitializer.java

package com.boraji.tutorial.spring.config;

import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

/**
 * @author imssbora
 */
public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

   @Override
   protected Class<?>[] getRootConfigClasses() {
      return new Class[] {};
   }

   @Override
   protected Class<?>[] getServletConfigClasses() {
      return new Class[] { WebConfig.class };
   }

   @Override
   protected String[] getServletMappings() {
      return new String[] { "/" };
   }
}

 

Build + Deploy + Run application

Use the following maven commands to build, deploy and run embedded Tomcat server.

mvn clean install  (This command triggers war packaging)

mvn tomcat7:run (This command run embedded tomcat and deploy war file automatically)

You can refer this link to learn how to run the above commands in Eclipse IDE.


Type the following URLs in browser's address bar to open the user input from.

http://localhost:8080/

Spring 4 MVC - Form validation example | BORAJI.COM

On validation failure, user input form will be displayed with error messages.

Spring 4 MVC - Form validation example | BORAJI.COM

On validation success, a new page will be displayed with user details as below.

Spring 4 MVC - Form validation example | BORAJI.COM