【问题标题】:How to: Spring get rid of @Validate for automatic Controller validation?如何:Spring 摆脱 @Validate 以进行自动控制器验证?
【发布时间】:2017-06-23 12:54:50
【问题描述】:

我知道 @Valid 注释,以指示 spring 在此示例中根据 JSR-303 验证例如 Controller 参数:

@GetMapping("/test")
public TestDTO testDTO(@Valid TestDTO testDTO){
        return testDTO;
}

但我希望能够以某种方式配置 Spring 以在我的所有控制器中启用验证,而无需明确指定 @Valid 注释。

这有可能吗?一些Spring配置?使用AOP?...

【问题讨论】:

标签: spring spring-mvc spring-boot spring-aop spring-validator


【解决方案1】:

我终于找到了一个可行的解决方案,从 Spring 配置的角度来看可能不是最佳解决方案(正如我所说的,我是 Spring 初学者)。

这个想法是修改参数解析器(实现 HandlerMethodArgumentResolver 的解析器),用 @RequestBody 注释替换与参数关联的参数解析器。从默认类(RequestResponseBodyMethodProcessor)创建一个继承类并覆盖类层次结构中的一个方法,该方法有效地确定是否执行验证(基于存在@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 类来替换默认参数解析器:

@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 以在您的 Application 配置类中交付您定制的 Bean:

@Configuration
@PropertySource("classpath:api.properties")
public class MyRestApiConfiguration {

    @Bean
    @Autowired
    RequestResponseBodyMethodProcessor requestResponseBodyMethodProcessor(List<HttpMessageConverter<?>> converters) {
        return new MyRequestResponseBodyMethodProcessor(converters);
    }

}

【讨论】:

  • 看起来不错。如果验证失败会怎样?
  • 在我的情况下,当验证失败时会抛出 MethodArgumentNotValidException,我在 handleMethodArgumentNotValidResponseEntityExceptionHandler 的覆盖方法)中捕获并处理它。
  • 谢谢!但是,对于那些 non-RequestBody 项如何自动执行呢?
【解决方案2】:

不幸的是,没有“合法”的方式来做到这一点。

此外@Valid 还不够。您还需要一个BindingResult 方法参数才能检查验证结果:bindingResult.hasErrors()

如果您不想使用BindingResult,您可以编写自己的验证器并在输入无效的情况下抛出异常。

【讨论】:

  • 谢谢。那么Aspects / Spring AOP呢?难道就不能这样实现吗?我正在学习春天。只希望开发者不用担心@Valid
  • BindingResult 不是必需的,具体取决于您正在构建的内容。例如,如果您尊重 JSR303 并使用 javax.validation,您可以将所有内容包装在您的 @ControllerAdvice 中,但我不知道是否有办法强制自动验证
  • 我找到了一种方法(不知道是合法还是非法)让它工作。查看接受的答案:)
【解决方案3】:

应该可以这样做,但是就像 Markus 在类似的 question 中一样,我不确定我是否同意它解决实际问题。

在不深入杂草的情况下,Spring 将验证作为模型绑定的一部分在ModelAttributeMethodProcessor'svalidateIfApplicable 方法中执行。根据 javadoc

如果适用,验证模型属性。默认实现 检查@javax.validation.Valid、Spring 的 Validated 和 custom 名称以“Valid”开头的注解。

要覆盖此功能,您需要创建自定义 ModelAttributeMethodProcessor / ServletModelAttributeMethodProcessor。然后您需要将其注册为参数解析器

public class ApplicationConfiguration extends WebMvcConfigurerAdapter {
    @Override
    public void addArgumentResolvers(final List<HandlerMethodArgumentResolver> argumentResolvers) {
        // add your custom model attribute processor here
    }
}

【讨论】:

  • 我无法通过这种机制实现它,但它给了我从哪里开始寻找如何去做的提示。我已经发布了我的解决方案,这可能不是最好的方式或选项,但我唯一可以让它发挥作用。
  • @GerardB 您是否遇到了使用此方法无效的特定问题?
  • 只需将我的解析器添加到argumentResolvers,这样就不会像忽略我那样工作。甚至不知道我的解析器是否真的被添加到托管解析器的RequestMappingHandlerAdapter;或者我的解析器被忽略,因为默认的 (RequestResponseBodyMethodProcessor) 首先匹配。
  • 感谢您提供更多信息。这表明您使用的@RequestBody/@ResponseBody 注释正确吗?
  • 是的,我在 @RequestBody 中应用它来进行 DTO 验证。
猜你喜欢
  • 2014-08-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-07-30
  • 1970-01-01
相关资源
最近更新 更多