【问题标题】:JSR-303 validation groups define a default groupJSR-303 验证组定义一个默认组
【发布时间】:2023-03-29 07:33:01
【问题描述】:

我有一个 bean,它有很多用 JSR-303 验证注释注释的字段。现在有一项新要求,即其中一个字段是强制性的,但仅限于某些条件。

我环顾四周,找到了我需要的验证组。

这就是我现在拥有的:

public interface ValidatedOnCreationOnly {
}

@NotNull(groups = ValidatedOnCreationOnly.class)
private String employerId;
@Length(max = 255)
@NotNull
private String firstName;
@Length(max = 255)
@NotNull
private String lastName;

但是,当我在单元测试中运行此验证时:

@Test
public void testEmployerIdCanOnlyBeSetWhenCreating() {
    EmployeeDTO dto = new EmployeeDTO();

    ValidatorFactory vf = Validation.buildDefaultValidatorFactory();
    Set<ConstraintViolation<EmployeeDTO>> violations = vf.getValidator().validate(dto, EmployeeDTO.ValidatedOnCreationOnly.class);

    assertEquals(violations.size(), 3);
}

事实证明,所有非组注释验证都被忽略了,我只得到 1 次违规。

我可以理解这种行为,但我想知道是否有一种方法可以使该组也包含所有未注释的参数。如果不是,我将不得不这样做:

public interface AlwaysValidated {
}

public interface ValidatedOnCreationOnly extends AlwaysValidated {
}

@NotNull(groups = ValidatedOnCreationOnly.class)
private String employerId;
@Length(max = 255, groups = AlwaysValidated.class)
@NotNull(groups = AlwaysValidated.class)
private String firstName;
@Length(max = 255, groups = AlwaysValidated.class)
@NotNull(groups = AlwaysValidated.class)
private String lastName;

我正在使用的真实类有更多的字段(大约 20 个),因此这种方法将指示验证的清晰方式变成了大杂烩。

谁能告诉我是否有更好的方法?可能是这样的:

vf.getValidator().validate(dto, EmployeeDTO.ValidatedOnCreationOnly.class, NonGroupSpecific.class);

我在一个 spring 项目中使用它,所以如果 spring 有另一种方式,我会很高兴知道。

【问题讨论】:

  • 在控制器上 @Validated( {ValidatedOnCreationOnly.class,Default.class}

标签: java spring validation bean-validation


【解决方案1】:

javax.validation.groups.Default 中有一个Default 组,代表默认的 Bean Validation 组。除非明确定义组列表:

  • 约束属于Default
  • 验证适用于Default

你可以扩展这个组:

public interface ValidatedOnCreationOnly extends Default {}

【讨论】:

  • 不错的答案。只是想知道我们是否创建了一个接口并使用 Default 扩展它,那么 spring 如何知道特定验证组不会对该特定字段执行验证。
【解决方案2】:

只是想添加更多:

如果你使用的是 spring 框架,你可以使用org.springframework.validation.Validator

@Autowired
private Validator validator;

并手动执行验证:

validator.validate(myObject, ValidationErrorsToException.getInstance());

在控制器中:

@RequestMapping(method = RequestMethod.POST)
public Callable<ResultObject> post(@RequestBody @Validated(MyObject.CustomGroup.class) MyObject request) {
    // logic
}

虽然以这种方式从 javax.validation.groups.Default 扩展是行不通的,但您必须将 Default.class 包含在组中:

class MyObject {

    @NotNull(groups = {Default.class, CustomGroup.class})
    private String id;

    public interface CustomGroup extends Default {}
}

【讨论】:

    【解决方案3】:

    对我来说到处添加 Default.class 不是好方法。 所以我扩展了 LocalValidatorFactoryBean ,它通过一些组进行验证,并在没有任何组的情况下委托进行验证。

    我使用的是 spring boot 2.2.6.RELEASE 我使用了 spring-boot-starter-validation 依赖项。

    我的 bean 用于验证

    public class SomeBean {
    
        @NotNull(groups = {UpdateContext.class})
        Long id;
        @NotNull
        String name;
        @NotNull
        String surName;
        String optional;
        @NotNull(groups = {CreateContext.class})
        String pesel;
        @Valid SomeBean someBean;
    }
    

    扩展LocalValidatorFactoryBean的自己的类的代码

    public class CustomValidatorFactoryBean extends LocalValidatorFactoryBean {
    
        @Override
        public void validate(Object target, Errors errors, Object... validationHints) {
            if (validationHints.length > 0) {
                super.validate(target, errors, validationHints);
            }
            super.validate(target, errors);
        }
    }
    
    

    通过 @Bean 或仅使用 @Component 将其放入 spring 上下文(如您所愿)

        @Bean
        @Primary
        public LocalValidatorFactoryBean customLocalValidatorFactoryBean() {
            return new CustomValidatorFactoryBean();
        }
    

    在一些 RestController 中的使用

    // So in this method will do walidation on validators with CreateContext group and without group
        @PostMapping("/create")
        void create(@RequestBody @Validated(CreateContext.class) SomeBean someBean) {
    
        }
    
        @PostMapping("/update")
        void update(@RequestBody @Validated(UpdateContext.class) SomeBean someBean) {
    
        }
    

    由于某种原因,当由 RestController 或其他 spring bean 调用 DummyService.testValidation() 时,testValidation 不起作用。 仅在 RestController 端有效:/

    @Validated
    @Service
    @AllArgsConstructor
    class DummyService {
      public void testValidation(@NotNull String string, @Validated(UpdateContext.class) SomeBean someBean) {
            System.out.println(string);
            System.out.println(someBean);
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-11-24
      • 1970-01-01
      • 2015-01-10
      • 2015-07-31
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多