Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring MVC @Valid Validation with custom HandlerMethodArgumentResolver

Tags:

I want to register a custom HandlerMethodArgumentResolver that could handle the following @Controller handler method definition

@RequestMapping(method = RequestMethod.POST)
public String createDomain(@Valid Domain domain, BindingResult errors, @RequestParam("countryId") Long countryId) {

I can register my resolver, which just creates a Domain object through request parameters, by overriding addArgumentResolver() from WebMvcConfigurerAdapter. When Spring tries to resolve the Domain parameter, it goes through its list of HandlerMethodArgumentResolver (there are a lot) and picks the first one that supports() it.

In the above example, although my resolver will get called and my Domain argument will get initialized, the @Valid annotation won't have been processed and the resolver for BindingResult, an ErrorsMethodArgumentResolver will fail because it requires a @ModelAttribute, @RequestBody or the @RequestPart argument in the handler method, which I don't have.

If I try to fix it by adding @ModelAttribute

@RequestMapping(method = RequestMethod.POST)
public String createDomain(@Valid @ModelAttribute Domain domain, BindingResult errors, @RequestParam("countryId") Long countryId) {

a HandlerMethodArgumentResolver implementation, ModelAttributeMethodProcessor, will get checked first with supports() and resolve the argument (with @ModelAttribute and @Valid) before my custom resolver. The BindingResult won't fail, but I won't have my custom creation behavior on the Domain instance.

I could just copy-paste the code for validation and adding to model that's in ModelAttributeMethodProcessor, but I was hoping there was an easier way to resolve my parameters and perform validation without adding an object to the model. Is there such a way?

like image 664
Sotirios Delimanolis Avatar asked Aug 06 '13 22:08

Sotirios Delimanolis


2 Answers

Nice description of the issue that you are facing.

I checked out the code that you have outlined and have come to the same conclusion that you have - there is no built-in way to have both a custom HandlerMethodArgumentResolver as well as @Valid related validation applied at the same time, the only choice is to do what the ModelAttributeMethodProcessor does which is to check if the parameter has a @Valid annotation and call the validation logic related code.

You can probably derive your HandlerMethodResolverArgumentResolver from ModelAttributeMethodProcessor and call super.validateIfApplicable(..) atleast this way the existing code is leveraged.

like image 79
Biju Kunjummen Avatar answered Sep 23 '22 18:09

Biju Kunjummen


It's may be too late, but your HandlerMethodArgumentResolver gets WebDataBinderFactory object as last argument, then, to hook up the validation, simply add this to your resolver implementation:

Object resolvedObject = // your logic 
if(parameter.hasParameterAnnotation(Valid.class){
            binderFactory.createBinder(webRequest,resolvedObject,"resolvedObjectLogicalName").validate ();
}
like image 31
Alexander.Furer Avatar answered Sep 20 '22 18:09

Alexander.Furer