Spring MVC Custom Validator Example

In spring mvc form submit tutorial, we learned about displaying displaying a form and submitting form data, including validating inputs using BindingResult.rejectValue(). In this example, we will learn to build a more robust validator for EmployeeVO model object. This validator is custom implementation of Validator interface. In this example, I am modifying the code used for form submit example we built in last tutorial.

Read More : Spring MVC Display, Validate and Submit Form Example

Custom Validator Implementation

Spring MVC supports validation by means of a validator object that implements the Validator interface. You can write the following validator to check if the required form fields are filled.

package com.howtodoinjava.demo.validator;

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

import com.howtodoinjava.demo.model.EmployeeVO;

@Component
public class EmployeeValidator implements Validator 
{

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

	public void validate(Object target, Errors errors) 
	{
		ValidationUtils.rejectIfEmptyOrWhitespace(errors, "firstName", "error.firstName", "First name is required.");
		ValidationUtils.rejectIfEmptyOrWhitespace(errors, "lastName", "error.lastName", "Last name is required.");
		ValidationUtils.rejectIfEmptyOrWhitespace(errors, "email", "error.email", "Email is required.");
	}

}

In this validator, you use utility methods such as rejectIfEmptyOrWhitespace() and rejectIfEmpty() in the ValidationUtils class to validate the required form fields. If any of these form fields is empty, these methods will create a field error and bind it to the field. The second argument of these methods is the property name, while the third and fourth are the error code and default error message.

Many times validation errors are not specific to fields. For example end date should be greater than start date. In this case, you can use the reject() method to create an object error to be bound to the EmployeeVO object, not to a field.

e.g. errors.reject(“invalid.dateDiff”, “End date should be greater than start date.”);

To activate this custom validator as a spring managed bean, you need to do one of following things:

1) Add @Component annotation to EmployeeValidator class and activate annotation scanning on the package containing such declarations.

<context:component-scan base-package="com.howtodoinjava.demo" />

2) Alternatively, you can register the validator class bean directly in context file.

<bean id="employeeValidator" class="com.howtodoinjava.demo.validator.EmployeeValidator" />

Controller Changes

To apply this validator, you need to perform the following modification to your controller.

1) Include validator reference to controller class and mark it autowired to ensure it’s available when needed.

@Autowired
EmployeeValidator validator;

2) Next change is in controller’s post method which is called when user submit the form.

@RequestMapping(method = RequestMethod.POST)
public String submitForm(@ModelAttribute("employee") EmployeeVO employeeVO,
						BindingResult result, SessionStatus status) 
{
	
	//Validation code
	validator.validate(employeeVO, result);
	
	//Check validation errors
	if (result.hasErrors()) {
		return "addEmployee";
	}
	
	//Store the employee information in database
	//manager.createNewRecord(employeeVO);
	
	//Mark Session Complete
	status.setComplete();
	return "redirect:addNew/success";
}

Once the validate method returns, the result parameter i.e. BindingResult contains the results for the validation process. You can check if there was any error in input using result.hasErrors() method call. If any error is detected you can again render the form view to let user correct it’s input.

If there is no error found then result.hasErrors() will return false, then you can simply process the input and redirect the user to next view.

Complete sourcecode foe controller used in this example is below:

package com.howtodoinjava.demo.controller;

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.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.bind.support.SessionStatus;

import com.howtodoinjava.demo.model.EmployeeVO;
import com.howtodoinjava.demo.service.EmployeeManager;
import com.howtodoinjava.demo.validator.EmployeeValidator;

@Controller
@RequestMapping("/employee-module/addNew")
@SessionAttributes("employee")
public class EmployeeController 
{
	@Autowired
	EmployeeManager manager;
	
	@Autowired
	EmployeeValidator validator;
	
	@RequestMapping(method = RequestMethod.GET)
    public String setupForm(Model model) 
	{
		 EmployeeVO employeeVO = new EmployeeVO();
		 model.addAttribute("employee", employeeVO);
		 return "addEmployee";
    }
	
	@RequestMapping(method = RequestMethod.POST)
    public String submitForm(@ModelAttribute("employee") EmployeeVO employeeVO,
            				BindingResult result, SessionStatus status) 
	{
		
		validator.validate(employeeVO, result);
		
		if (result.hasErrors()) {
			return "addEmployee";
		}
		//Store the employee information in database
		//manager.createNewRecord(employeeVO);
		
		//Mark Session Complete
		status.setComplete();
		return "redirect:addNew/success";
    }
	
	@RequestMapping(value = "/success", method = RequestMethod.GET)
    public String success(Model model) 
	{
		 return "addSuccess";
    }
}

Drop me you questions into comments section.

Happy Learning !!

Was this post helpful?

Join 7000+ Awesome Developers

Get the latest updates from industry, awesome resources, blog updates and much more.

* We do not spam !!

12 thoughts on “Spring MVC Custom Validator Example”

  1. What if we are taking about a nested Object? For instance, let’s say we have a UserDto with the following fields:
    String name (which is simple)
    String password (simple also)
    Address address -> with the following fields in its class: StreetName, streetNumber.

    How do you access the nested object’s fields from within UserValidator class?

    Reply
  2. Can we perform validation on a List of RequestBody object?
    Custom Collection Validator’s “supports()” method does not support the entities within the collection.

    Reply
  3. Nice article.
    i am getting problem in fetching value from database. I have create simple login page i am having problem in if we insert username and password it must check from database whether it is valid or not for multiple user. just like facebook first registered the login with that details. please provide me the code.

    Reply
  4. package com.tcs.controller;
    
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    import javax.validation.Valid;
    
    import org.json.JSONArray;
    import org.json.JSONException;
    import org.json.JSONObject;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Controller;
    import org.springframework.validation.BindingResult;
    import org.springframework.validation.MapBindingResult;
    import org.springframework.validation.ObjectError;
    import org.springframework.web.bind.WebDataBinder;
    import org.springframework.web.bind.annotation.InitBinder;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    import org.springframework.web.bind.annotation.ResponseBody;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
    
    import com.google.gson.Gson;
    import com.tcs.beans.Order;
    import com.tcs.dao.OrderDAO;
    import com.tcs.service.OrderServive;
    import com.tcs.validator.OrderValidator;
    
    @Controller
    
    public class OrderController extends WebMvcConfigurerAdapter {
    	@Autowired
    	OrderServive service1;
    	private OrderValidator orderValidator;
    
    	public OrderController() {
    		super();
    		System.out.println("inside the system controller");
    		orderValidator = new OrderValidator();
    
    	}
    
    	@InitBinder
    	public void initBinder(WebDataBinder binder) {
    		binder.setValidator(orderValidator);
    		System.out.println("inside initbinder");
    	}
    
    	@RequestMapping(value="/AddOrder",method = RequestMethod.POST,produces="application/json")
    public @ResponseBody List<Order> AddOrder(@Valid @RequestBody Order order) throws JSONException{
    		System.out.println("inside add");
    Gson gson=new Gson();
    Order o=new Order();
    		JSONObject j=new JSONObject(order);
    		o=gson.fromJson(j.toString(), Order.class);
    		System.out.println(o);
    		String msg=service1.addOrder(order);
    		return service1.getOrders();
    		
    	}
    
    	@RequestMapping(value = "/getOrders", method = RequestMethod.POST, produces = "application/json")
    	public @ResponseBody
    	List<Order> getOrders() throws JSONException {
    		List<Order> ordList = new ArrayList<Order>();
    		ordList = service1.getOrders();
    		for (Order o : ordList) {
    			System.out.println(o);
    		}
    		return ordList;
    
    	}
    
    	@RequestMapping(value = "/UpdateOrder", method = RequestMethod.POST, produces = "application/json")
    	public @ResponseBody
    	List<Order> UpdateOrder(@RequestBody Order order) {
    		Gson gson = new Gson();
    		Order o = new Order();
    		JSONObject j = new JSONObject(order);
    		o = gson.fromJson(j.toString(), Order.class);
    		System.out.println("in update" + o);
    		String st = service1.UpdateOrder(o);
    		System.out.println(st);
    		List<Order> ordList = service1.getOrders();
    		return ordList;
    
    	}
    
    	@RequestMapping(value = "/DeleteOrder", method = RequestMethod.POST, produces = "application/json")
    	public @ResponseBody
    	List<Order> DeleteOrder(@RequestBody Order order) {
    		Gson gson = new Gson();
    		Order o = new Order();
    		JSONObject j = new JSONObject(order);
    		o = gson.fromJson(j.toString(), Order.class);
    		String st = service1.DeleteOrder(o);
    		System.out.println(st);
    		List<Order> ordList = service1.getOrders();
    		return ordList;
    	}
    
    	@RequestMapping(value = "/GloDel", method = RequestMethod.POST, produces = "application/json")
    	public @ResponseBody
    	List<Order> GloDel(@RequestBody String ordList) throws JSONException {
    		String st;
    		Gson gson = new Gson();
    		Order o = new Order();
    		JSONArray j = new JSONArray(ordList);
    		for (int i = 0; i < j.length(); i++) {
    			o = gson.fromJson(j.get(i).toString(), Order.class);
    			st = service1.DeleteOrder(o);
    			System.out.println(st);
    		}
    		return service1.getOrders();
    
    	}
    
    	@RequestMapping(value = "/GloUpd", method = RequestMethod.POST, produces = "application/json")
    	public @ResponseBody
    	List<Order> GloUpd(@RequestBody String ordList) throws JSONException {
    		String st;
    		Gson gson = new Gson();
    		Order o = new Order();
    		JSONArray j = new JSONArray(ordList);
    		for (int i = 0; i < j.length(); i++) {
    			o = gson.fromJson(j.get(i).toString(), Order.class);
    			st = service1.UpdateOrder(o);
    			System.out.println(st);
    		}
    		List<Order> ordList1 = service1.getOrders();
    		for (Order o3 : ordList1) {
    			System.out.println(o3);
    		}
    		return service1.getOrders();
    
    	}
    }
    

    How do we display the spring validation errors on front end form using angular JS.

    Reply
  5. Hi,

    Nice article. What is the purpose of status.setComplete(); ? when do we use sessionstatus class. please give some scenario.

    Regards,Asad

    Reply
    • You see “@SessionAttributes("employee")” at controller level, SessionStatus.setComplete(); clear all such attributes.

      When @SessionAttribute is not used, a new command object will be created on each request, even when rendering the form again due to binding errors. If this annotation is enabled, the command object will be stored in the session for subsequent uses, until you call setComplete() method.

      If you do not call this method explicitly, then variable will be stored in session forever.

      Reply
  6. Hi Lokesh, nice publication. I have a question about this validations, suppose you have two classes (User and Address, this classes has a one-to-one cardinality ) and you have a user form registration in the view. What is the best practice to validate all fields (of both classes) at the same time when the user press the “save” button?
    Thanks in advance buddy.

    Reply

Leave a Comment

HowToDoInJava

A blog about Java and related technologies, the best practices, algorithms, and interview questions.