Validating a Spring MVC @Controller with a custom validation class - the easy way

For some reason this is terribly documented on the web .. so, I hope this helps someone.

Assuming we have the following model:

public class PersonForm {

    private String name;
    private int age;


To perform more advanced validation, we could write a custom validator class:

public class PersonFormValidator implements Validator {

     * This Validator validates *just SomeModel instances
     public boolean supports(Class clazz) {
   	return PersonForm.class.equals(clazz);

     public void validate(Object obj, Errors e) {
    	PersonForm personForm = (PersonForm) obj;
        // for the purpose of this demo
        // we're going to reject this value all the time
        e.rejectValue("age", "age.rejected"); 



  • Notice that our validator implements Validator
    • So it also has to implement the methods: supports and validate
  • We use e.rejectValue("<field>", "<message>"); to reject this field

Finally, our controller might look something like this:

public class PersonController{
    protected void initBinder(WebDataBinder binder) {
           binder.setValidator(new PersonFormValidator());
    @RequestMapping(value = "/person", method = RequestMethod.POST)
    public String submitPerson(
        @Valid @ModelAttribute("PersonForm") 
        PersonForm personForm,
        BindingResult binding, 
        HttpSession session,
        RedirectAttributes redirectAttributes) {

           	return "/pages/person";
	    return "/pages/personSuccess";


  • @IinitBinder sets the validator for this @Controller to be our PersonFormValidator.
    • Remember: supports(Class clazz) in PersonFormValidator tells the controller which models are validated with this validator
  • The line: @Valid @ModelAttribute("PersonForm") PersonForm personForm tells the controller to validate the PersonForm object. Results from the validation are stored in BindingResult binding
  • The @Valid annotation will validate our model PersonForm using the annotations within the class (e.g.: @NotBlank, @min and @max as well as running the validation defined in PersonFormValidator.validate()
  • Finally, we check if there are errors and deal with them accordingly with binding.hasErrors()

Making sure it all works

import static;
import static org.springframework.test.web.server.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.server.result.MockMvcResultMatchers.model;
import static org.springframework.test.web.server.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.server.result.MockMvcResultMatchers.view;

public class PersonControllerTest {

    MockMvc mockMvc;

    PersonController controller;
    public void setUp() throws Exception {
        InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
        this.mockMvc = MockMvcBuilders.standaloneSetup(controller)
	                .setMessageConverters(new MappingJackson2HttpMessageConverter()).build();

    public void testSubmitPerson_failsValidation() 
      throws Exception {
		.param("name", "Joe")
		.param("age", "100")
          .attributeHasFieldErrors("personForm", "age"))
             .andExpect( view().name("/pages/person") )
             .andExpect( status().isOk() )


  • Standard setup for mockMvc in setup()
  • .andExpect(model().attributeHasFieldErrors("personForm", "age")) does most of the work here - it ensures that the "age" field on our "personForm" has an error raised against it.
  • I included the imports for the various Builders, Matchers and Handlers .. cause, for some reason, they're often a little tricksy to find in my IDE (might be the same for you ;) ).

.. and that should do the trick :).

In summary

  • Create a model (PersonForm)
  • Create a custom validation class for validation our model (PersonFormValidator)
  • In your @Controller, bind your validator to the controller using the @InitBinder annotation and binder.setValidator
  • in your @Controller method, Use the @Valid annotation to validate the incoming model.
  • You can test the results with the MockMvc and model().attributeHasFieldErrors() to ensure that validation fails in the correct cases.