目前在軟件開發過程當中 , 先後端基本是經過數據接口方式進行交互 . 做爲一名後端程序員 , 對前端的同事提交數據的合法性進行校驗儼然是一個必不可少的環節 .前端
而Spring Boot 或者 Spring MVC 中已經集成了 用於 校驗JSR 303 規範 (Bean Validation 1.0) 的jar包 , 咱們內置許多事先定義好的validator , 讓咱們僅經過註解的方式即可以對接口參數進行校驗 .java
這裏以一個登陸接口爲例 :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
其中各個註解的做用能夠參考下表 :app
註解 | 做用 |
---|---|
@Null | 被標記的字段必須爲null |
@NotNull | 被標記的字段必須不爲null (沒法檢查長度爲0的字符串) |
@NotBlank | 被標記的字符串被trim後的長度不能爲0 |
@NotEmpty | 被標記的字段不能爲null 或者 empty (適用於String , Collection , Map , Array) |
註解 | 做用 |
---|---|
@Size(min=?, max=?) | 被標記的字段的長度大小必需要在min 與max 指定的範圍內 (適用於String , Collection , Map , Array) |
@Length(min=?, max=?) | 被標記的字符串長度必需要爲在min 與max 指定的範圍內 |
下表註解支持的日期類型包括 java 8 之前的 Date
對象 , 以及 java 8的LocalDateTime
系列 . 具體能夠參考註解的java doc 註釋 .ide
註解 | 做用 |
---|---|
@Past | 被標記的日期字段必須在當前時間以前 |
@PastOrPresent | 被標記的日期字段必須在當前時間以前或等於當前時間 |
@Future | 被標記的日期字段必須在當前時間以後 |
@FutureOrPresent | 被標記的日期字段必須在當前時間或等於當前時間 |
註解 | 做用 |
---|---|
@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 值 , inclusive 爲true 表示等於value 值也符合條件 , 反之則不符合 . (適用於BigDecimal , BigInteger ,String , byte , short , int , long 以及他們的包裝類型) |
@DecimalMin(value=? ,inclusive=true ) | 被標記的字段必需要大於(等於)value 值 , inclusive 爲true 表示等於value 值也符合條件 , 反之則不符合 .(適用於BigDecimal , BigInteger ,String , byte , short , int , long 以及他們的包裝類型) |
@Digits(integer=?,fraction=?) | 被標記的字段必需要爲符合指定格式的數字,interger指定整數精度,fraction指定小數精度。 |
@Range(min=?, max=?) | 被標記的字段必須在min 與max 範圍內 , (適用於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 以及他們的包裝類型) |
註解 | 做用 |
---|---|
被標記的字段必需要爲郵件地址格式 , 若是是null , 會算做經過校驗 , 通常與@NotNull 搭配使用 (適用於字符串) |
|
@Pattern(regexp=?) | 被標記的字段必需要符合regexp 指定的正則表達式 (適用於字符串) |
單憑一個註解確定沒法進行對字段進行校驗 , 閱讀文檔可知 . 註解的校驗邏輯均編寫在對應的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便可 . 這裏我編寫一個用於校驗身份證格式的註解 . 以下所示 :
/** * @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 {};
}
複製代碼
/** * @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 註解便編寫完成了 .