【问题标题】:Entity validation before the "beforeSave" RepositoryEventHandler fired“beforeSave” RepositoryEventHandler 触发之前的实体验证
【发布时间】:2017-02-28 00:04:03
【问题描述】:

Spring Data REST docs 描述了在RepositoryEventHandler 被触发之后验证实体的方式:

您只需向 bean 的工作是正确的事件

之后调用验证器

有没有办法在实体被传递到“beforeSave”RepositoryEventHandlers 之前应用[声​​明性] JSR-303 验证?

从我目前所看到的调试来看,情况并非如此,并且“beforeSave”RepositoryEventHandlers 在任何验证发生之前被触发。

我可以直接在处理程序中编写验证调用,但这与处理“之后”验证的方式不同。

顺便说一句。事件处理程序调用的顺序似乎在 Spring Boot 1.3.8 和 1.5.1 之间发生了变化。过去,验证发生在@HandleBeforeSave 处理程序之前。在 1.5.1 中,ValidatingRepositoryEventListener@HandleBeforeSave 处理程序之后触发

更新:

正如 cmets 中所述,Spring Data REST Jira 中似乎有一个 ticket 对此是开放的。

【问题讨论】:

    标签: spring validation spring-data-rest


    【解决方案1】:

    作为一种解决方法:

    1. 创建扩展ValidatingRepositoryEventListenerPreflightValidatingRepositoryEventListener 并使用@Order(Ordered.HIGHEST_PRECEDENCE) 对其进行注释。

      @Component
      @Order(Ordered.HIGHEST_PRECEDENCE)
      public class PreflightValidatingRepositoryEventListener extends ValidatingRepositoryEventListener {
          public PreflightValidatingRepositoryEventListener(ObjectFactory<PersistentEntities> persistentEntitiesFactory) {
              super(persistentEntitiesFactory);
          }
      }
      
    2. 添加与 ValidatingRepositoryEventListener 相同的验证器

      @Configuration
      @Import(RepositoryRestMvcConfiguration.class)
      public class Config extends RepositoryRestConfigurerAdapter {
      
          @Bean
          @Primary
          public Validator validator() {
              return new LocalValidatorFactoryBean();
          }
      
          @Autowired
          private PreflightValidatingRepositoryEventListener preflightValidatingRepositoryEventListener;
      
          @Override
          public void configureValidatingRepositoryEventListener(ValidatingRepositoryEventListener validatingListener) {
              Validator validator = validator();
      
              validatingListener.addValidator("beforeCreate", validator);
              validatingListener.addValidator("beforeSave", validator);
      
              preflightValidatingRepositoryEventListener.addValidator("beforeCreate", validator);
              preflightValidatingRepositoryEventListener.addValidator("beforeSave", validator);
          }
      }
      

    注意:验证将运行两次

    【讨论】:

    • 谢谢!似乎是一个巧妙的解决方法。我的验证可能不会在事件处理程序之后运行。所以我最终得到了一个很好的旧方面:scottfrederick.cfapps.io/blog/2012/03/09/…
    • 我今天正在处理这个问题。我的第一个成功是我在这里提出的方式,但后来我只想做一个方面将@Order 添加到 ValidatingRepositoryEventListener,但没有成功。你能分享一下你的想法吗?
    • 当然,在答案中分享。
    【解决方案2】:

    作为替代解决方法,这里有一个完全基于this 示例的验证方面(稍作更改)。不要忘记添加@EnableAspectJAutoProxy 以激活切面并将其放入扫描Spring 配置的包中。

    @Aspect
    @Component
    public class RestRepositoryValidationAspect {
    
        @Autowired
        private Validator validator;
    
        @Pointcut("@annotation(org.springframework.data.rest.core.annotation.HandleBeforeCreate)")
        private void beforeCreateInvocation() {
        }
    
        @Pointcut("@annotation(org.springframework.data.rest.core.annotation.HandleBeforeSave)")
        private void beforeSaveInvocation() {
        }
    
    
        @Around("beforeCreateInvocation() || beforeSaveInvocation()")
        public Object validateBeforeRepostioryEventHandler(ProceedingJoinPoint joinPoint) throws Throwable {
    
            MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
            Method method = methodSignature.getMethod();
            Annotation[][] argAnnotations = method.getParameterAnnotations();
            String[] argNames = methodSignature.getParameterNames();
            Object[] args = joinPoint.getArgs();
    
            for (int i = 0; i < args.length; i++) {
              if (hasValidAnnotations(argAnnotations[i])) {
                validateArg(args[i], argNames[i]);
              }
            }
    
            return joinPoint.proceed(args);
    
        }
    
        private boolean hasValidAnnotations(Annotation[] annotations) {
            if (annotations.length < 1) {
                return false;
            }
    
            for (Annotation annotation : annotations) {
                if (Valid.class.isInstance(annotation)) {
                    return true;
                }
            }
            return false;
        }
    
        private void validateArg(Object arg, String argName) {
            BindingResult result = getBindingResult(arg, argName);
            validator.validate(arg, result);
            if (result.hasErrors()) {
                throw new RepositoryConstraintViolationException(result);
            }
        }
    
        private BindingResult getBindingResult(Object target, String targetName) {
            return new BeanPropertyBindingResult(target, targetName);
        }
    
    }
    

    需要验证的存储库事件处理程序参数必须用@Valid注解标记,例如

    @HandleBeforeSave
    public void handleSave(@Valid MyEntity myEntity) {
        ...
    }
    

    【讨论】:

      猜你喜欢
      • 2017-06-24
      • 1970-01-01
      • 2014-09-09
      • 2015-07-04
      • 1970-01-01
      • 1970-01-01
      • 2018-06-29
      • 1970-01-01
      • 2021-08-08
      相关资源
      最近更新 更多