1. 程式人生 > 其它 >javax validation

javax validation

技術標籤:java日常小結java

使用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);

結果
在這裡插入圖片描述
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