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:
- Include the bean-validation-api JAR in your classpath. This gets you the annotation.
- Include a Bean Validation implementation JAR (e.g., the reference implementation, hibernate-validator) in your classpath. This enables the validation framework.
- 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
- 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;
- 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.