JSR-303验证组定义默认组

时间:2016-02-12 08:59:51

标签: java spring validation bean-validation

我有一个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);

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

3 个答案:

答案 0 :(得分:21)

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

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

您可以扩展此群组:

public interface ValidatedOnCreationOnly extends Default {}

答案 1 :(得分:1)

只想添加更多内容:

如果您使用的是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 {}
}

答案 2 :(得分:1)

对我来说,到处添加 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);
    }
}