如何:Spring 摆脱 @Validate 以进行自动控制器验证?
How to: Spring get rid of @Validate for automatic Controller validation?
我知道 @Valid
注释指示 spring 根据 JSR-303 验证控制器参数,例如在这个例子中:
@GetMapping("/test")
public TestDTO testDTO(@Valid TestDTO testDTO){
return testDTO;
}
但我希望能够以某种方式配置 Spring 以在我的所有控制器中启用验证,而无需明确指定 @Valid
注释。
这有可能吗?一些 Spring 配置?利用AOP?...
遗憾的是没有 "legal" 方法。
此外@Valid
还不够。您还需要一个 BindingResult
方法参数来检查验证结果:bindingResult.hasErrors()
如果您不想使用 BindingResult
,您可以编写自己的验证器并在输入无效时抛出异常。
应该可以做到这一点,但是就像 Markus 在类似的 question 中一样,我不确定我是否同意它解决了实际问题。
在不深入讨论的情况下,Spring 在 ModelAttributeMethodProcessor's validateIfApplicable 方法中执行验证作为模型绑定的一部分。根据 javadoc
Validate the model attribute if applicable. The default implementation
checks for @javax.validation.Valid, Spring's Validated, and custom
annotations whose name starts with "Valid".
要覆盖此功能,您需要创建自定义 ModelAttributeMethodProcessor / ServletModelAttributeMethodProcessor。然后您需要将其注册为参数解析器
public class ApplicationConfiguration extends WebMvcConfigurerAdapter {
@Override
public void addArgumentResolvers(final List<HandlerMethodArgumentResolver> argumentResolvers) {
// add your custom model attribute processor here
}
}
我终于找到了一个可行的解决方案,从 Spring 配置的角度来看可能不是最佳解决方案(正如我所说,我是 Spring 初学者)。
想法是修改参数解析器(实现 HandlerMethodArgumentResolver
的参数解析器),用 @RequestBody
注释替换与参数关联的参数解析器。从默认的 class 创建继承的 class 并覆盖 class 层次结构中的方法,该方法有效地确定是否执行验证(基于 @Valid
、@Validated
、@ValidXxxxxx
注释作为默认行为),使得始终验证而无需进一步检查。
所以这是代码(我正在使用 Java 8 BTW):
扩展 RequestResponseBodyMethodProcessor
以定义验证策略(在本例中,始终验证):
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 ;)
}
}
定义 @Configuration
class 替换默认参数解析器的位置:
@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);
}
}
最后配置 Spring 以在您的应用程序配置中交付您的自定义 Bean class:
@Configuration
@PropertySource("classpath:api.properties")
public class MyRestApiConfiguration {
@Bean
@Autowired
RequestResponseBodyMethodProcessor requestResponseBodyMethodProcessor(List<HttpMessageConverter<?>> converters) {
return new MyRequestResponseBodyMethodProcessor(converters);
}
}
我知道 @Valid
注释指示 spring 根据 JSR-303 验证控制器参数,例如在这个例子中:
@GetMapping("/test")
public TestDTO testDTO(@Valid TestDTO testDTO){
return testDTO;
}
但我希望能够以某种方式配置 Spring 以在我的所有控制器中启用验证,而无需明确指定 @Valid
注释。
这有可能吗?一些 Spring 配置?利用AOP?...
遗憾的是没有 "legal" 方法。
此外@Valid
还不够。您还需要一个 BindingResult
方法参数来检查验证结果:bindingResult.hasErrors()
如果您不想使用 BindingResult
,您可以编写自己的验证器并在输入无效时抛出异常。
应该可以做到这一点,但是就像 Markus 在类似的 question 中一样,我不确定我是否同意它解决了实际问题。
在不深入讨论的情况下,Spring 在 ModelAttributeMethodProcessor's validateIfApplicable 方法中执行验证作为模型绑定的一部分。根据 javadoc
Validate the model attribute if applicable. The default implementation checks for @javax.validation.Valid, Spring's Validated, and custom annotations whose name starts with "Valid".
要覆盖此功能,您需要创建自定义 ModelAttributeMethodProcessor / ServletModelAttributeMethodProcessor。然后您需要将其注册为参数解析器
public class ApplicationConfiguration extends WebMvcConfigurerAdapter {
@Override
public void addArgumentResolvers(final List<HandlerMethodArgumentResolver> argumentResolvers) {
// add your custom model attribute processor here
}
}
我终于找到了一个可行的解决方案,从 Spring 配置的角度来看可能不是最佳解决方案(正如我所说,我是 Spring 初学者)。
想法是修改参数解析器(实现 HandlerMethodArgumentResolver
的参数解析器),用 @RequestBody
注释替换与参数关联的参数解析器。从默认的 class 创建继承的 class 并覆盖 class 层次结构中的方法,该方法有效地确定是否执行验证(基于 @Valid
、@Validated
、@ValidXxxxxx
注释作为默认行为),使得始终验证而无需进一步检查。
所以这是代码(我正在使用 Java 8 BTW):
扩展 RequestResponseBodyMethodProcessor
以定义验证策略(在本例中,始终验证):
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 ;)
}
}
定义 @Configuration
class 替换默认参数解析器的位置:
@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);
}
}
最后配置 Spring 以在您的应用程序配置中交付您的自定义 Bean class:
@Configuration
@PropertySource("classpath:api.properties")
public class MyRestApiConfiguration {
@Bean
@Autowired
RequestResponseBodyMethodProcessor requestResponseBodyMethodProcessor(List<HttpMessageConverter<?>> converters) {
return new MyRequestResponseBodyMethodProcessor(converters);
}
}