Wednesday, March 28, 2012

Spring Form Validation with Validator Interface

Spring 3 has a native validation support and this short post shows how it can be used.

As stated in the Spring reference itself:
  • validation should not be tied to the web tier
  • it should be easy to localize
  • it should be possible to plug in any validator available
Therefore, Spring has Validator interface and it can be used in any layer of the application.

Let's say you have a Company entity and you need a validation for it...

public class Company{

 private Long id; 
 private String name;
 private String address;
 private String contactEmail;
 private String websiteUrl;
 private String description;
 private String username;
 private String password;

 // getters and setters...
}

First, you need to do is to create a CompanyValidatior class which implements a Validator interface.

package com.demo.springmvcjpa.validation;

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

import com.demo.springmvcjpa.model.Company;

public class CompanyValidator implements Validator {

 public boolean supports(Class clazz) {
  return Company.class.isAssignableFrom(clazz);
 }

 public void validate(Object target, Errors errors) {
  ValidationUtils.rejectIfEmptyOrWhitespace(errors, "name", "name.required"); 
  ValidationUtils.rejectIfEmptyOrWhitespace(errors, "contactEmail", "email.required");
  ValidationUtils.rejectIfEmptyOrWhitespace(errors, "address", "address.required");
 }

}

As you have noticed, there are 2 methods in Validator interface that need to be implemented:
1) supports - checks if this validator can validate instances of the supplied class, and
2) validate - which validates the given object and logs errors in Errors object (if any).

There's ValidationUtils helper class with a couple of useful methods: rejectIfEmpty and rejectIfEmptyOrWhitespace.

Sure, you need to register your bean by adding:


You'll also need to modify the CompanyController class:
package com.demo.springmvcjpa.controller;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;

import com.demo.springmvcjpa.model.Company;
import com.demo.springmvcjpa.service.CompanyService;
import com.demo.springmvcjpa.validation.CompanyValidator;

@Controller
@RequestMapping("/company")
public class CompanyController {

 private static final Logger logger = LoggerFactory.getLogger(CompanyController.class);

 CompanyService companyService;

 @Autowired
 public void setCompanyService(CompanyService companyService) {
  this.companyService = companyService;
 }
 
 @Autowired
 private CompanyValidator validator;

 @ModelAttribute("company")
 public Company init(@PathVariable String id) {
  return companyService.find(id);
 }
 
 @RequestMapping(value="/{id}", method=RequestMethod.GET)
 public ModelAndView view(ModelAndView mv, @ModelAttribute("company") Company company) {
  logger.info("requesting company");
  mv.addObject("company", company);
  mv.setViewName("company/save");
  return mv;
 }

 @RequestMapping(value="/{id}", method=RequestMethod.POST)
 public String update(ModelAndView mv, @ModelAttribute("company") Company company, BindingResult result) {
     logger.info("updating company");
     validator.validate(company, result);
     if (result.hasErrors()) {
      return "company/save";
     } else {
      return "redirect:/spring/company/details/" + companyService.save(company).getId();
     }
 }

        // other methods...
 
}
And finally, to display errors in the view you need to add line:

This is the case when you want to show the errors altogether on top of the form (see the example below). You may want to add error message beside after each form field (or something similar), in which case you would replace * value for path attribute with corresponding property name.
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="tags" tagdir="/WEB-INF/tags" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ page session="false" %>

 
 
 
 
Name:
Description:
Address:
Email:
Website URL:
Username:
Password:

Note that class="required", type="url" and possibly some other attributes are part of Client side validation and are not necessary. If you want to read more about Client Side Validation with jQuery Validator plugin you may want to read this post.

Dont't forget to add error messages to your messages.properties file:

company.name.required=Name is required
company.address.required=Address is required simple
company.email.required=Email is required

This is how validation may look like for simple objects. I'll write more about validation for composite objects.


No comments:

Post a Comment