首页 > 解决方案 > 包装列表的自定义验证抛出 UnexpectedTypeException: HV000030

问题描述

我一直对在对象列表上使用的自定义验证器感到头疼。该示例是借用代码,但使用包装类 ValidList 来嵌入实际列表。现在,当我使用像 Size(min=1) 这样的 ootb 注释时,验证有效。但是,如果我使用自定义注释(不要查看功能,这里仅用于演示),我会收到一个 500 错误,带有臭名昭著的 HV000030:

No validator could be found for constraint 'com.example.demo.validation.ListLength' validating type 'java.util.List<SomePojo>'. Check configuration for 'list' 

当我打电话时

$ curl -H 'Content-Type: application/json' -X POST -d '[{"id":1,"token":"lalala"}]' http://localhost:8080/pojos

(我使用 Lombok 的 @Value 来实现 getter/setter 和不变性)

有谁知道怎么回事?

@RestController
public class SomeController {

    @PostMapping("/pojos")
    public ValidList<SomePojo> test(@Valid @RequestBody ValidList<SomePojo> pojos) {
        return pojos;
    }
}
@Value
public class ValidList<SomePojo> {
    @JsonValue
    @ListLength
    //@Size(min=1)
    private List<SomePojo> values;

    @JsonCreator
    public ValidList(SomePojo... items) {
        this.values = Arrays.asList(items);
    }
}
@Value
public class SomePojo {
    int id;
    String token;
}
@Documented
@Constraint(validatedBy = ListValidator.class)
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface ListLength {

    String message() default "Not enough pojos";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};

    @Target({ElementType.FIELD, ElementType.PARAMETER})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @interface List {
        ListLength[] value();
    }
}
public class ListValidator implements ConstraintValidator<ListLength, List<SomePojo>> {

    @Override
    public boolean isValid(List<SomePojo> somePojos, ConstraintValidatorContext constraintValidatorContext) {
        return somePojos.size() > 1;
    }
}

标签: javaspringvalidation

解决方案


该异常的原因是在定义中SomePojo用作通用参数。您应该在实现中对泛型类型使用通配符。ListListValidatorConstraintValidator

从javadoc复制:

实现必须遵守以下限制:

  • T必须解析为非参数化类型
  • 或泛型参数T必须是无界通配符类型

所以你应该这样重写ListValidator

public class ListValidator implements ConstraintValidator<ListLength, List<?>> {

    @Override
    public boolean isValid(List<?> somePojos, ConstraintValidatorContext constraintValidatorContext) {
        return somePojos.size() > 1;
    }
}

有关更多信息,请查看ConstraintValidatorjavadocBean 验证文档


推荐阅读