Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring 4 @Validation annotation with BindingResult is not working

When I use BindingResult with @Validated annotation in a method, validation is not working. If I remove BindingResult from the method parameters, it works fine. I used @ControllerAdvice.

Any idea why is it not working?

My code is below:

public ResponseEntity<?> dologin(
        @Validated({ Group.Login.class }) @RequestBody User user,
        BindingResult errors)
{
    // somecode
}

As per the Validation, Data Binding, and Type Conversion documentation, any validation message will be automatically added to the binder’s BindingResult.

There will be no impact if I remove it, right?

like image 357
bNd Avatar asked Mar 17 '17 08:03

bNd


1 Answers

Since you only provided the code from your controller method, I'm adding some additional information that is needed in order to get validation working in a Spring 4 application, so you can see where any differences might be. I am minimally assuming the following:

  1. Include the bean-validation-api JAR in your classpath. This gets you the annotation.
  2. Include a Bean Validation implementation JAR (e.g., the reference implementation, hibernate-validator) in your classpath. This enables the validation framework.
  3. Your request mapping could be of one the following forms:

@RequestMapping(method = RequestMethod.POST)
public String dologin(@Valid @ModelAttribute("userForm") User user, final BindingResult bindingResult) throws Exception

@RequestMapping(method = RequestMethod.POST)
public String dologin(@Validated @ModelAttribute("userForm") User user, final BindingResult bindingResult) throws Exception

or, if using validation groups:


@RequestMapping(method = RequestMethod.POST)
public String dologin(@Validated({ User.Login.class }) @ModelAttribute("userForm") User user, final BindingResult bindingResult) throws Exception

  1. Your annotation in the User class should be inline with message property keys:

@NotBlank(message = "{firstName.required}")
@Size(min = 2, max = 24, message = "{firstName.size}")
@Pattern(regexp = "^[’' \\.\\-\\p{L}\\p{M}]*$", message = "{firstName.format}")
private String firstName;

or, if using validation groups:


public interface Login extends Default {}

@NotBlank(message = "{firstName.required}", groups = {Login.class})
@Size(min = 2, max = 24, message = "{firstName.size}", groups = {Login.class})
@Pattern(regexp = "^[’' \\.\\-\\p{L}\\p{M}]*$", message = "{firstName.format}", groups = {Login.class})
private String firstName;

  1. Your message properties should be in src/main/resources:

firstName.required=First name is required.
firstName.size=First name must be between {min} and {max} characters long.
firstName.format=First name may contain only letters, apostrophes, spaces and hyphens.

Make sure your Bean Validation specification version matches your implementation version. Bean Validation API 2.0 was released in August 2017. The Bean Validation 2.0 Reference Implementation is Hibernate Validation Engine 6.0.

The validation errors will be added to the BindingResult if that is present in the method parameters. Alternatively, you can use Errors in place of BindingResult - the API is essentially the same. Both of these work equivalently in my above implementation.

However, if I leave the parameter out of my controller method, I see (from logging) that the validation is triggered and the appropriate errors are raised and the mapping of my message keys to their properties succeed, but my Server Error page is rendered instead of my expected view. For additional information on the requirement of this parameter, see this related question on the BindingResult/Errors parameter.


Aug 31, 2017 2:21:56 PM com.test.spring.controller.ErrorController handleException
SEVERE: Server Error
org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 2 errors
Field error in object 'userForm' on field 'firstName': rejected value []; codes [Size.userForm.firstName,Size.firstName,Size.java.lang.String,Size]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [userForm.firstName,firstName]; arguments []; default message [firstName],24,2]; default message [First name must be between 2 and 24 characters long.]
Field error in object 'userForm' on field 'firstName': rejected value []; codes [NotBlank.userForm.firstName,NotBlank.firstName,NotBlank.java.lang.String,NotBlank]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [userForm.firstName,firstName]; arguments []; default message [firstName]]; default message [First name is required.]
    at org.springframework.web.method.annotation.ModelAttributeMethodProcessor.resolveArgument(ModelAttributeMethodProcessor.java:117)

I see very consistent behavior in all of the above combinations.

One difference I note is that you are using @ControllerAdvice in your example. Most of my controllers use the @Controller annotation (local controller errors), not @ControllerAdvice (used for global errors, such as for rendering HTTP 404 or 500 error pages). See this related question/answer for the use of @ControllerAdvice.

In the above error log, you see that ErrorController is the one reporting the validation errors if I leave out the BindingResult/Errors parameter, and indeed that is my controller class that uses @ControllerAdvice annotation instead of @Controller. By using @ControllerAdvice on your login controller you may be interfering with normal error handling.

Since you are using the Hibernate-specific @Validated group validation annotation instead of the JSR-303/349/380 specification @Valid annotation, there may be some differences between the implementation you used (I'm guessing version 5, which conformed to API 1.1). I am using version 6.0.2, which conforms to the new (August 2017) Bean Validation Framework API 2.0.

like image 200
vallismortis Avatar answered Oct 06 '22 00:10

vallismortis