Today’s entry inaugurates our “tips & tricks” category. The tips or tricks that we will be discussing may not be marvels of engineering or anything profound in theoretical computing, however, they are very useful in practice and should be in the toolkit of every practitioner.
The topic for this post is the
@Validated Spring annotation for the validation of parameters in the methods of a MVC controller. Since version 3.0 of the Spring framework, validation support was enhanced and improved including support for JSR-303 bean validation and correspondent annotations. We are not going to talk here about the JSR-303 bean validation per se. Instead, we will concentrate on a Spring variant of validation. Spring defines
@Validated annotation as a variant of JSR-303
“…supporting the specification of validation groups. Designed for convenient use with Spring’s JSR-303 support but not JSR-303 specific.”
When Spring MVC is configured to use this type of validation (an example can be found in the code attached) it is really easy to apply
@Validated to the controllers’ method parameters to reap the benefits of automatic validation.
All you need to do is to define a validator, as follows:
…and then annotate your controller’s method parameter accordingly:
That’s really it, your validator will be called before any work is done in the method’s body and if validation fails the caller will get a response with code 400 – i.e. bad request.
Now, if you want you can change the response in the exception handler method of the controller as shown below:
If you like this new feature, you may want to apply this to any controller’s method parameters. You could, for example, write something like the following:
Unfortunately, if you were to run the above mentioned code, you would quickly realize that validation is not happening. So what is going on here?
As it turns out every parameter in the controller’s mapped method is handled by a separate argument resolver implementation, one for
@PathVariable annotated parameter, another for
@RequestParam annotated and so on.
Every such resolver has its own logic of creating and initializing the DataBinder that is behind all/part of the magic related to validation. It appears that Spring (at least 3.x) implements logic that involves execution of validators only for the argument resolvers that apply to the method parameters annotated with
So, is this it? Does the implementation of parameter validation with
@Validated fall short of its promise? Actually, no. Do not despair!
It turns out that there is an annotation (
@ModelAttribute) that is sometimes used to build an object from the parameters in the URI.
Let’s try something like this:
The use of the
@ModelAttribute on a controller’s method parameter invokes a chain of argument resolvers.
The first resolver that will be called invokes the method createPair, which in turn invokes the argument resolvers for the path variables. The last resolver that is invoked is the ModelAttributeMethodProcessor.
It also so happens that the ModelAttributeMethodProcessor has the necessary logic to create and initialize a DataBinder, in such a way that performs any validations, if present.
So, if the above method is written as follows:
…and if there is a validator that supports the Pair class then such validator will be executed.
By now you are probably wondering how is this relevant to the fact that we cannot use @Validated on the method parameters other than ones annotated with
@RequestBody and, as we discovered,
Well, here comes the trick that saves the day. If you annotate any controller’s method parameter with @ModelAttribute it forces Spring to apply ModelAttributeMethodProcessor to the parameter and, hence, validation will be performed, if validation is applicable.
The only thing you need is to use the following construct:
Now, the Spring framework will happily validate your ID because it was tricked into thinking that it is a model attribute object and, hence, it will apply the IdValidator, as specified, to it. So, that’s great, right? We get our validation, for free!
Well, almost for free. We get the convenience of parameter validation with a bit of extra code. Moreover, the exception that needs to be handled, in case of any validation errors on a parameter annotated with
@ModelAttribute, is BindException instead of MethodArgumentNotValidException – and no, they have no common parent class, or interface, in case you are wondering, so you would need to add another handler. Nevertheless, the above extra code seems to be a small price to pay for the convenience of validating method parameters.
Credits: Anton Mokshyn – amokshyn (at) copyright (dot) com. Anton provided the code of composite validator.