Search code examples
javaspring

Spring MVC @Valid Validation with custom HandlerMethodArgumentResolver


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?


Solution

  • 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.