How to: Spring get rid of @Validate for automatic controller validation?
I know about annotation @Valid
to give spring command to check for example controller argument according to JSR-303 in this example:
@GetMapping("/test")
public TestDTO testDTO(@Valid TestDTO testDTO){
return testDTO;
}
But I would like to be able to customize spring in some way to enable validation in all my controllers without explicitly specifying the annotation @Valid
.
Is this possible anyway? Some spring configuration? Using AOP? ...
source to share
I finally came across a working solution that might not be optimal in terms of Spring configuration (since I said I am a Spring newbie).
The idea was to change the arguments arguments (the ones that implement HandlerMethodArgumentResolver
) by replacing the argument argument associated with the arguments with annotation @RequestBody
. Creating a legacy default class ( RequestResponseBodyMethodProcessor
) and override the method in the class hierarchy, which effectively determines whether to perform validation or not (based on the presence @Valid
, @Validated
, @ValidXxxxxx
annotation as the default behavior), allowing you to always check with no further verification.
So here's the code (I'm using Java 8 BTW):
Expand RequestResponseBodyMethodProcessor
to define a validation strategy (in this case, always check):
public class MyRequestResponseBodyMethodProcessor extends RequestResponseBodyMethodProcessor {
public MyRequestResponseBodyMethodProcessor(List<HttpMessageConverter<?>> converters) {
super(converters);
}
@Override
protected void validateIfApplicable(WebDataBinder binder, MethodParameter parameter) {
binder.validate(); // always validating @RequestMapping annotated parameters ;)
}
}
Define the class @Configuration
where to replace the default argument recognizer:
@Configuration
public class MyValidationAdapterConfigurer {
@Autowired
private RequestMappingHandlerAdapter requestMappingHandlerAdapter;
// Injecting your own resolver
@Autowired
private RequestResponseBodyMethodProcessor requestResponseBodyMethodProcessor;
@PostConstruct
public void init() {
// Don't know why but, removing the target resolver and adding the injected one to the end does not work!
// Must be something related with the resolvers ordering. So just replacing the target in the same position.
final List<HandlerMethodArgumentResolver> mangledResolvers = requestMappingHandlerAdapter.getArgumentResolvers().stream()
.map(resolver -> resolver.getClass().equals(RequestResponseBodyMethodProcessor.class) ?
requestResponseBodyMethodProcessor: resolver)
.collect(Collectors.toList());
requestMappingHandlerAdapter.setArgumentResolvers(mangledResolvers);
}
}
Finally, configure Spring to provide a customized Bean in your application config class:
@Configuration
@PropertySource("classpath:api.properties")
public class MyRestApiConfiguration {
@Bean
@Autowired
RequestResponseBodyMethodProcessor requestResponseBodyMethodProcessor(List<HttpMessageConverter<?>> converters) {
return new MyRequestResponseBodyMethodProcessor(converters);
}
}
source to share
Unfortunately, there is no "legal" way to do this.
It is also @Valid
not enough. You also need a method parameter BindingResult
to check the result of the check:bindingResult.hasErrors()
If you don't want to use BindingResult
, you can write your own validator and throw an exception in case of invalid input.
source to share
This should be possible, however, like Markus in a similar question , I'm not sure if I agree that he solves the real problem.
Without getting too far into the weeds, Spring does validation as part of the model binding in the ModelAttributeMethodProcessor method validateIfApplicable. Per javadoc
Validate the model attribute, if applicable. The default implementation checks for @ javax.validation.Valid, Spring Checked and executed annotations starting with "Valid".
To override this functionality, you need to create your own ModelAttributeMethodProcessor / ServletModelAttributeMethodProcessor. Then you will need to register it as a resolver argument
public class ApplicationConfiguration extends WebMvcConfigurerAdapter {
@Override
public void addArgumentResolvers(final List<HandlerMethodArgumentResolver> argumentResolvers) {
// add your custom model attribute processor here
}
}
source to share