javax validation

使用protobuf时,对参数的校验很不方便,多是要手动编写一堆if-else的判断条件。介绍下使用javax validation来做参数校验,减少代码量、提供开发效率、定制统一的错误返回结果。

原生的校验注解有:

注解 作用
@Size 判断集合或字符的大小
@NotEmpty 判断集合或字符不能为空
@Pattern 判断正则表达式是否满足
@PastOrPresent 判断日期类型是否小于等于某时
@Past 判断日期类型是否小于某时
@FutureOrPresent 判断日期类型是否大于等于某时
@Future 判断日期类型是否大于某时
@DecimalMin 判断数值类型的最小值
@DecimalMax 判断数值类型的最大值
@Digits 判断数值类型的最大位数和小数位数
@Min 判断数值的最小值
@Max 判断数值的最大值
@PositiveOrZero 判断数值是否非负
@NegativeOrZero 判断数值是否非正
@Negative 判断数值是否是负数
@Positive 判断数值是否是正数
@AssertTrue 判断布尔类型是否为true
@AssertFalse 判断布尔类型是否为false
@Email 判断字符串是否是正确的邮箱格式
@NotBlank 判断字符不能为空、不能含义空字符
@Null 判断为null
@NotNull 判断不能为null

注解中有message属性用于自定义校验错误的输出内容。

要想使用javax validation对controller层接收的参数对象进行校验,使用protobuf接口的难处理,所有建议转成javabean后对象,bean对象校验。
下面的例子介绍了的基本应用。
引用MapStruct文档(十二)——protobuf映射protobuf2和javabean转换的例子。

@NotNull
@Min(value = 2)
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER})
@Retention(RUNTIME)
@Documented
@Constraint(validatedBy = {})
public @interface Min2 {
 
    String message() default "";
 
    Class<?>[] groups() default {};
 
    Class<? extends Payload>[] payload() default {};
 
}
 
@Target({ FIELD, METHOD, PARAMETER, ANNOTATION_TYPE, TYPE_USE })
@Retention(RUNTIME)
@Constraint(validatedBy = CheckCaseValidator.class)
@Documented
public @interface CheckCase {
 
    String message() default "格式不匹配";
 
    Class<?>[] groups() default { };
 
    Class<? extends Payload>[] payload() default { };
 
    CaseMode value();
 
}
 
public enum CaseMode {
 
    UPPER,
    LOWER;
}
 
public interface Group1 {
}
 
 
public interface Group2 {
}
 
 
/**
 * 验证字符类型的字段是否都是大写或小写。ConstraintValidator<A extends Annotation, T>中的T是要支持的属性类型。
 */
public class CheckCaseValidator implements ConstraintValidator<CheckCase, String> {
 
    private CaseMode caseMode;
 
    @Override
    public void initialize(CheckCase constraintAnnotation) {
        caseMode = constraintAnnotation.value();
    }
 
    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        boolean isValid;
        if (value == null) {
            return true;
        }
        if (caseMode == CaseMode.UPPER) {
            isValid = value.equals(value.toUpperCase());
        } else {
            isValid = value.equals(value.toLowerCase());
        }
        if (!isValid) {
            /*ConstraintValidatorContext通常用户自定义错误信息。
            类似于spring中注入一个bean。在验证中现有的类无法实现某些功能,额外的导入其他支持要求的类。
            这里要求可以在返回的错误中添加参数。但官方不建议使用ConstraintValidatorContext#buildConstraintViolationWithTemplate,
            因为自定义消息模板是被直接传递到表达式语言引擎中的,可能会造成漏洞;建议使用HibernateConstraintValidatorContext;
            Hibernate Validator可以进行处理转义,并且不会执行EL表达式。*/
            HibernateConstraintValidatorContext hibernateConstraintValidatorContext =
                    context.unwrap(HibernateConstraintValidatorContext.class);
            hibernateConstraintValidatorContext.disableDefaultConstraintViolation();
            hibernateConstraintValidatorContext.addExpressionVariable("validatedValue", value)
                    .buildConstraintViolationWithTemplate("${validatedValue}" + "不满足" + caseMode.name())
                    .addConstraintViolation();
        }
        return isValid;
    }
}
 
 
@Data
@GroupSequence({TestDTO.class, Group1.class, Group2.class})
public class TestDTO {
 
    @Min2
    private Integer id;
 
    @CheckCase(value = CaseMode.UPPER)
    private String name;
 
    private Date createTime;
 
    private TypeEnum downloadResourceTypeEnum;
 
    @Valid
    @ConvertGroup(from = Default.class, to = Group2.class)
    private ItemDTO mainItem;
 
    @AssertTrue(groups = Group1.class)
    private Boolean disable;
 
    @Digits(integer = 2, fraction = 1)
    private BigDecimal prePrice;
 
    private Map<String, String> kv;
 
    private String oneString;
 
    private Integer oneInt;
 
    @Size(max = 5, min = 1, groups = Group2.class)
    private List<ItemDTO> itemList;
 
    private List<String> numberList;
 
    private List<TypeEnum> typesList;
 
    private List<Integer> nosList;
}
 
@Data
public class ItemDTO {
 
    @Min(value = 100)
    private Long itemId;
 
    @DecimalMax(value = "350", message = " ${formatter.format('%1$.2f', validatedValue)} 大于 {value}", groups = Group2.class)
    private Double price;
 
    @NotNull(groups = Group2.class, message = "给我个值")
    private Integer uint32Count;
 
    private Long uint64Count;
 
    private Integer sint32Count;
 
    private Long sint64Count;
 
    private Integer fixed32Count;
 
    private Long fixed64Count;
 
    private Integer sfixed32Count;
 
    private Long sfixed64Count;
 
    private byte[] type;
     
}
 
@Resource
private Validator validator;
 
Test.Builder builder = Test.newBuilder();
builder.setName("ss");
builder.setDisable(false);
builder.setPrePrice(1);
Item.Builder itemBuilder = Item.newBuilder();
itemBuilder.setItemId(1);
builder.setMainItem(itemBuilder);
TestDTO testDTO = testMapper.toDTO(builder.build());
 
Set<ConstraintViolation<TestDTO>> validate = validator.validate(testDTO);
System.out.println(validate);

结果
javax validation
AssertTrue#groups定义的是校验组,校验组是个接口,用去区别不同字段在什么情况下校验;

@GroupSequence定义的是校验组顺序,前面组校验不通过后面的组就不会校验;不设置校验组,都是属于Default.class的默认组;指定类的@GroupSequence,要用类本身取代Default.class;

@ConvertGroup只能和@Valid连用,用于验证对象属性时,校验其中的那组;

@Min2是一个组合校验;

@CheckCase是自定义的校验。

Validation.buildDefaultValidatorFactory().getValidator().validate(T object, Class<?>… groups)就是校验的方法,object是被校验的对象,groups可以指定校验组。

message = " KaTeX parse error: Expected '}', got 'EOF' at end of input: …tter.format('%1.2f’, validatedValue)} 大于 {value}"中可以使用EL表达式。@NotNull上不用使用。

Hibernate Validator

上一篇:Python深度学习笔记06--机器学习基础


下一篇:idea build项目出现错误“Error: java: 程序包javax.validation不存在”