I know about @Valid
annotation to instruct spring to validate for example a Controller argument according to JSR-303 in such this example:
@GetMapping("/test")
public TestDTO testDTO(@Valid TestDTO testDTO){
return testDTO;
}
But I would like to be able to configure Spring in some way to enable validation in all my controllers without specify explicitly the @Valid
annotation.
Is that possible in any way? Some Spring configuration? Making use of AOP?...
I have finally came across with a working solution which may be not the optimal from the point of view of Spring configuration (as I said I'm Spring beginner).
The idea was to modify the argument resolvers (the ones that implement HandlerMethodArgumentResolver
), replacing the argument resolver associated to arguments with a @RequestBody
annotation. Creating an inherited class from the default one (which is RequestResponseBodyMethodProcessor
) and overriding a method in the class hierarchy which efectively determines if perform a validation or not (based in the presence of @Valid
, @Validated
, @ValidXxxxxx
annotations as the default behaviour), making to always validate with no further check.
So here is the code (I'm using Java 8 BTW):
Extend RequestResponseBodyMethodProcessor
to define validation strategy (in this case, always validate):
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 a @Configuration
class where to replace default argument resolver:
@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 deliver your customized Bean in your Application configuration class:
@Configuration
@PropertySource("classpath:api.properties")
public class MyRestApiConfiguration {
@Bean
@Autowired
RequestResponseBodyMethodProcessor requestResponseBodyMethodProcessor(List<HttpMessageConverter<?>> converters) {
return new MyRequestResponseBodyMethodProcessor(converters);
}
}
unfortunately there is no "legal" way to do it.
Furthermore @Valid
is not enough. You need also a BindingResult
method parameter to be able to check the validation result: bindingResult.hasErrors()
If you don't want to use BindingResult
, you could write your own Validator and throw an Exception in case of invalid input.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With