聊聊jsr-303接口參數校驗

前言

目前在軟件開發過程當中 , 先後端基本是經過數據接口方式進行交互 . 做爲一名後端程序員 , 對前端的同事提交數據的合法性進行校驗儼然是一個必不可少的環節 .前端

而Spring Boot 或者 Spring MVC 中已經集成了 用於 校驗JSR 303 規範 (Bean Validation 1.0) 的jar包 , 咱們內置許多事先定義好的validator , 讓咱們僅經過註解的方式即可以對接口參數進行校驗 .java

image-20191127150019486

快速上手

這裏以一個登陸接口爲例 :git

UserLoginDTO :程序員

@Data
@EqualsAndHashCode(callSuper = false)
public class UserLoginDTO implements Serializable {

    private static final long serialVersionUID = 1L;

    @NotBlank(message = "帳號不能爲空")
    @Size(min = 6, max = 10, message = "帳號長度在6-10位之間")
    private String account;

    @NotBlank(message = "密碼不能爲空")
    private String password;
}
複製代碼

接口 :正則表達式

/** * 登陸接口 */
@PostMapping
public LoginSuccessVO login(@RequestBody @Valid UserLoginDTO loginDTO) {
    return loginService.login(loginDTO);
}
複製代碼

這裏只須要在DTO的字段上加上validation包的註解 , 一個字段能夠被多個註解所標記 , 例如示例中的@NotBlank , @Size . 而後在做爲controller方法的入參處使用@Valid 註解進行標記便可 . 如上所示 , 這樣當請求進入到controller方法內部前 , 就會對前端傳過來的參數進行校驗判斷 . 若校驗不經過 , 則便拋出異常 , 且message爲註解中message屬性指定的消息 .後端

註解做用列表

validation 包中可用的註解以下圖所示 :markdown

image-20191128010347431.png

其中各個註解的做用能夠參考下表 :app

  • 空值判斷
註解 做用
@Null 被標記的字段必須爲null
@NotNull 被標記的字段必須不爲null (沒法檢查長度爲0的字符串)
@NotBlank 被標記的字符串被trim後的長度不能爲0
@NotEmpty 被標記的字段不能爲null 或者 empty (適用於String , Collection , Map , Array)
  • 長度判斷
註解 做用
@Size(min=?, max=?) 被標記的字段的長度大小必需要在minmax指定的範圍內 (適用於String , Collection , Map , Array)
@Length(min=?, max=?) 被標記的字符串長度必需要爲在minmax指定的範圍內
  • 日期檢查

下表註解支持的日期類型包括 java 8 之前的 Date 對象 , 以及 java 8的LocalDateTime系列 . 具體能夠參考註解的java doc 註釋 .ide

註解 做用
@Past 被標記的日期字段必須在當前時間以前
@PastOrPresent 被標記的日期字段必須在當前時間以前或等於當前時間
@Future 被標記的日期字段必須在當前時間以後
@FutureOrPresent 被標記的日期字段必須在當前時間或等於當前時間
  • boolean 檢查
註解 做用
@AssertFalse 被標記的字段必需要爲false
@AssertTrue 被標記的字段必需要爲true
  • 數值檢查

下表註解當被標記的字段爲null時 , 會經過校驗 , 因此通常搭配@NotNull進行使用 .oop

註解 做用
@Min(value=?) 被標記的字段必需要大於value值(適用於BigDecimal , BigInteger , byte , short , int , long 以及他們的包裝類型)
@Max(value=?) 被標記的字段必需要小於value值(適用於BigDecimal , BigInteger , byte , short , int , long 以及他們的包裝類型)
@DecimalMax(value=? ,inclusive=true ) 被標記的字段必需要小於(等於)value值 , inclusivetrue表示等於value值也符合條件 , 反之則不符合 . (適用於BigDecimal , BigInteger ,String , byte , short , int , long 以及他們的包裝類型)
@DecimalMin(value=? ,inclusive=true ) 被標記的字段必需要大於(等於)value值 , inclusivetrue表示等於value值也符合條件 , 反之則不符合 .(適用於BigDecimal , BigInteger ,String , byte , short , int , long 以及他們的包裝類型)
@Digits(integer=?,fraction=?) 被標記的字段必需要爲符合指定格式的數字,interger指定整數精度,fraction指定小數精度。
@Range(min=?, max=?) 被標記的字段必須在minmax範圍內 , (適用於BigDecimal , BigInteger , byte , short , int , long 以及他們的包裝類型)
@Positive 被標記的字段必須爲正數 (適用於BigDecimal , BigInteger ,String , byte , short , int , long 以及他們的包裝類型)
@PositiveOrZero 被標記的字段必須爲正數或0 (適用於BigDecimal , BigInteger ,String , byte , short , int , long 以及他們的包裝類型)
@Negative 被標記的字段必須爲負數 (適用於BigDecimal , BigInteger ,String , byte , short , int , long 以及他們的包裝類型)
@NegativeOrZero 被標記的字段必須爲負數或0 (適用於BigDecimal , BigInteger ,String , byte , short , int , long 以及他們的包裝類型)
  • 格式檢查
註解 做用
@Email 被標記的字段必需要爲郵件地址格式 , 若是是null , 會算做經過校驗 , 通常與@NotNull搭配使用 (適用於字符串)
@Pattern(regexp=?) 被標記的字段必需要符合regexp指定的正則表達式 (適用於字符串)

自定義校驗註解與validator

單憑一個註解確定沒法進行對字段進行校驗 , 閱讀文檔可知 . 註解的校驗邏輯均編寫在對應的Validator類中 , 這裏以@NotNull爲例 . 其校驗註解對應的validator以下所示 :

/** * Validate that the object is not {@code null}. * * @author Emmanuel Bernard */
public class NotNullValidator implements ConstraintValidator<NotNull, Object> {

	@Override
	public boolean isValid(Object object, ConstraintValidatorContext constraintValidatorContext) {
		return object != null;
	}
}
複製代碼

能夠看到 , 上面@NotNull的validator 實現了一個叫ConstraintValidator的接口 . 以下所示 :

/** * <A> 爲註解類型 * <T> 爲被校驗的字段類型 */
public interface ConstraintValidator<A extends Annotation, T> {

	/** * 該方法用於讀取註解中傳遞的值 , 初始化到Validator中 . */
	default void initialize(A constraintAnnotation) {
	}

	/** * 該方法用於編寫校驗邏輯,返回true表示校驗經過 , false表示不經過 . */
	boolean isValid(T value, ConstraintValidatorContext context);
}
複製代碼

同理 , 若是咱們想實現本身的jsr-303規範的校驗註解 , 自定義一個註解並實現一個validator便可 . 這裏我編寫一個用於校驗身份證格式的註解 . 以下所示 :

  1. 自定義一個註解
/** * @author wukun * @since 2019/11/26 */
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
@Retention(RUNTIME)
@Documented
@Constraint(validatedBy = {IdCardValidator.class}) // 必須使用 @Constraint註解標記自定義註解, 並在validatedBy中指定用於校驗的validator類型 , 可指定多個.
public @interface IdCard {

    // 是否必傳,默認必傳. (該字段爲自定義字段)
    boolean required() default true;

    // 如下三個字段爲必備字段,直接複製過來便可 . 
    String message() default "IdCard format is invalid!";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}
複製代碼
  1. 自定義validator類
/** * @author wukun * @since 2019/11/26 */
public class IdCardValidator implements ConstraintValidator<IdCard, String> {

    //用於接收註解上自定義的 required
    private boolean required;

    @Override
    public void initialize(TodayOrAfter constraintAnnotation) {
        required = constraintAnnotation.required();
    }

    @Override
    public boolean isValid(LocalDateTime value, ConstraintValidatorContext context) {
        if (Objects.isNull(value) && required) {
            return false;
        } else {
            //此處編寫校驗邏輯 ....
        }
        
    }
}
複製代碼

到此 , 自定義jsr 303 註解便編寫完成了 .

參考鏈接

相關文章
相關標籤/搜索