前言
數據效驗工作在開發工作中,是非常重要的,保證數據的正確性,可靠性,安全性。不僅在前端進行效驗,還要在后台繼續進行效驗。
前端做驗證只是為了用戶體驗,比如控制按鈕的顯示隱藏,單頁應用的路由跳轉等等。后端才是最終的保障。總之,一切用戶的輸入都是不可信的。
常見的驗證方式
前端的校驗是必須的,這個很簡單,因為客戶體驗。后台的校驗更是必須的,關鍵在於如何與目前我們的分層思想(控制層、業務層、持久層)綜合起來考慮。
在每層都要進行校驗嗎?還是只在是某個特定層做就可以了? 是否有好的校驗框架(如前端的jquery校驗框架、springmvc校驗框架)?
總之校驗框架還是有很多的,原理不就是對后端接收的數據進行特定規則的判斷,那我們怎么制定規則,有怎么去檢驗呢?
1、表現層驗證:SpringMVC提供對JSR-303的表現層驗證;
2、業務邏輯層驗證:Spring3.1提供對業務邏輯層的方法驗證(當然方法驗證可以出現在其他層,但筆者覺得方法驗證應該驗證業務邏輯);
3、DAO層驗證:Hibernate提供DAO層的模型數據的驗證(可參考hibernate validator參考文檔的7.3. ORM集成)。
4、數據庫端的驗證:通過數據庫約束來進行;
5、客戶端驗證支持:JSR-303也提供編程式驗證支持。
Hibernate-validator
本章主要介紹一下hibernate-validator,下面就一起共同的去學習吧。
what?
注意:hibernate-validator 與 持久層框架
hibernate沒有什么關系,hibernate-validator 是 hibernate 組織下的一個開源項目。
hibernate-validator是JSR 380(Bean Validation 2.0)、JSR 303(Bean Validation 1.0)規范的實現。
JSR 380-Bean Validation 2.0定義了一個實體和方法驗證的元數據模型和 API。JavaEE(改名為:Jakarta EE)中制定了 validation 規范,即:javax.validation-api(現為 jakarta.validation-api,jar 包的名字改變,包里面的包名、類名未變,因此使用方式不變)包,
spring-boot-starter-web、spring-boot-starter-webflux包都已引入此依賴,直接使用即可。有點類似於 slf4j 與 logback(log4j2)的關系,使用的時候,代碼中使用
javax.validate提供的接口規范功能,加載的時候,根據 SPI 規范加載對應的規范實現類。它和
hibernate沒什么關系,放心大膽的使用吧。
使用的注解
標識注解
1. @Vaild()
標記用於驗證級聯的屬性、方法參數或方法返回類型。在驗證屬性、方法參數或方法返回類型時,將驗證在對象及其屬性上定義的約束。此行為是遞歸應用的。
2.@Validated()
spring 提供的擴展注解,可以方便的用於分組校驗.
約束注解
下面除了列出的參數,每個約束都有參數 message,groups 和 payload。這是 Bean Validation 規范的要求。
其中,message是提示消息,groups可以根據情況來分組。
以下每一個注解都可以在相同元素上定義多個。
3. @AssertFalse: 檢查元素是否為 false,【支持數據類型:boolean、Boolean】
4. @AssertTrue: 檢查元素是否為 true,【支持數據類型:boolean、Boolean】
5. @DecimalMax(value=, inclusive=):【支持數據類型:BigDecimal、BigInteger、CharSequence、(byte、short、int、long 和其封裝類)】
inclusive: boolean,默認 true,表示是否包含,是否等於。
value:① 當 inclusive=false ,檢查帶注解的值是否小於指定的最大值。② 當 inclusive=true 檢查該值是否小於或等於指定的最大值。參數值是根據 bigdecimal 字符串表示的最大值。
6. @DecimalMin(value=, inclusive=):【支持數據類型:BigDecimal、BigInteger、CharSequence、(byte、short、int、long 和其封裝類)】
inclusive: boolean,默認 true,表示是否包含,是否等於
value: ① 當 inclusive=false 時,檢查帶注解的值是否大於指定的最大值。② 當 inclusive=true 檢查該值是否大於或等於指定的最大值。參數值是根據 bigdecimal 字符串表示的最小值。
7. @Digits(integer=, fraction=):【支持的數據類型: BigDecimal, BigInteger, CharSequence, byte, short, int, long 、原生類型的封裝類、任何 Number 子類。】
檢查值是否為最多包含 integer 位整數和 fraction 位小數的數字
8. @Email ("regexp")【支持的數據類型:CharSequence】
檢查指定的字符序列是否為有效的電子郵件地址。可選參數 regexp 和 flags 允許指定電子郵件必須匹配的附加正則表達式(包括正則表達式標志)。
9. @Max(value=):【支持的數據類型: BigDecimal, BigInteger, byte, short, int, long, 原生類型的封裝類, CharSequence 的任意子類(字符序列表示的數字), Number 的任意子類,
javax.money.MonetaryAmount 的任意子類】
檢查值是否 小於或等於 指定的 最大值
10. @Min(value=):【支持的數據類型: BigDecimal, BigInteger, byte, short, int, long, 原生類型的封裝類, CharSequence 的任意子類(字符序列表示的數字), Number 的任意子類,
javax.money.MonetaryAmount 的任意子類】
檢查值是否 大於或等於 指定的 最大值
11. @NotBlank:【支持數據類型:CharSequence】
檢查字符序列 是否為空,以及去空格后的長度是否大於 0。 與@NotEmpty 的不同之處在於,此約束只能應用於字符序列,並且忽略尾隨空格。
12. @NotNull:【支持數據類型:任何類型】
檢查值是否 不為null
13. @NotEmpty:【支持數據類型:CharSequence, Collection, Map, arrays】
檢查元素是否為 null 或 空
14. @Size(min=, max=)和:@Length(min=, max=)【支持數據類型:CharSequence,Collection,Map, arrays】
檢查元素個數是否在 min(含)和 max(含) 之間
15. @Negative:【支持數據類型: BigDecimal, BigInteger, byte, short, int, long, 原生類型的封裝類, CharSequence 的任意子類(字符序列表示的數字), Number 的任意子類,
javax.money.MonetaryAmount 的任意子類】
檢查元素是否 嚴格 為負數。零值被認為無效。
16. @NegativeOrZero:【支持數據類型:BigDecimal, BigInteger, byte, short, int, long, 原生類型的封裝類, CharSequence 的任意子類(字符序列表示的數字), Number 的任意子類,
javax.money.MonetaryAmount 的任意子類】
檢查元素是否為 負或零。
17. @Positive:【支持數據類型: BigDecimal, BigInteger, byte, short, int, long, 原生類型的封裝類, CharSequence 的任意子類(字符序列表示的數字), Number 的任意子類,
javax.money.MonetaryAmount 的任意子類】
檢查元素是否 嚴格 為正。零值被視為無效。
18. @PositiveOrZero:【支持數據類型: BigDecimal, BigInteger, byte, short, int, long, 原生類型的封裝類, CharSequence 的任意子類(字符序列表示的數字), Number 的任意子類,
javax.money.MonetaryAmount 的任意子類】
檢查元素是否 為正或零。
19.@Null:【支持數據類型:任何類型】
檢查值是否為 null
20.@Future:【支持的數據類型:java.util.D
檢查日期是否在 未來。就是大於當前日期。
21. @FutureOrPresent:【支持數據類型:同@Future】
檢查日期是現在或將來,大於等於當前日期
22. @Past:【支持數據類型:同@Future】
檢查日期是否在過去,小於當前日期
23.@PastOrPresent:【支持數據類型:同@Future】
檢查日期是否在過去或現在,小於等於當前日期。
24. @Pattern(regex=, flags=):【支持數據類型:CharSequence】
根據給定的 flag 匹配,檢查字符串是否與正則表達式 regex 匹配
25. @CreditCardNumber:【支持數據類型:String】
校驗信用卡號碼
26. @NotEmptyPattern(regex=):【支持數據類型:String】
在字符串不為空的情況下,驗證是否匹配正則表達式
27. @ListStringPattern(regex=):【支持數據類型:List<String>】
驗證集合中的字符串是否滿足正則表達式
28. @DateValidator(regex=):【支持數據類型:String】
驗證日期格式是否滿足正則表達式,Local為ENGLISH
29. @DateFormatCheckPattern(regex=):【支持數據類型:String】
驗證日期格式是否滿足正則表達式,Local為自己手動指定
Hibernate-validator的使用
一、導入maven依賴
<dependency><groupId>javax.validation</groupId> <artifactId>validation-api</artifactId> <version>1.1.0.Final</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-validator</artifactId> <version>5.2.0.Final</version> </dependency>
二、通用的效驗工具
import com.google.common.base.Preconditions; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.wj.exception.ParamException; import org.apache.commons.collections.MapUtils; import javax.validation.*; import javax.validation.groups.Default; import java.util.*; public class BeanValidator { private static Validator validator=null; //獲取Validator對象 static{ ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory(); validator = validatorFactory.getValidator(); } /** * 通過參數,判斷是那種效驗:對象 集合 * @param obj 效驗的bean * @param objects 效驗的集合 * @return */ public static Map<String,String> validate(Object obj, Object... objects){ if (objects != null && objects.length > 0){ return validateList(Lists.asList(obj, objects)); }else { return validateObject(obj, new Class[0]); } } /** * 對象數據效驗 * 使用指定分組 * @param t 被效驗的bean * @param groups 分組 * @param <T> * @return 返回錯誤信息 */ public static <T>Map<String,String> validateObject(T t, Class... groups){ //如果分組為空,使用默認的分組 if (groups == null){ groups = new Class[]{Default.class}; } //獲取實體類驗證后的信息,存放在Set集合。 //ConstraintViolation類封裝着實體類的每個屬性的效驗之后的信息 Set<ConstraintViolation<T>> validateResult = validator.validate(t, groups); if (validateResult.isEmpty()){ //如果屬性都符合要求,沒有錯誤信息,返回一個空集合 return Collections.emptyMap(); }else { HashMap errors = Maps.newHashMap(); //遍歷集合 Iterator iterator = validateResult.iterator(); while (iterator.hasNext()){ ConstraintViolation violation = (ConstraintViolation) iterator.next(); //將每個屬性的錯誤信息,添加到HashMap集合中 errors.put(violation.getPropertyPath().toString(), violation.getMessage()); } return errors; } } /** * 檢查集合中的bean * @param collection 被效驗的集合 * @return */ public static Map<String, String> validateList(Collection<?> collection){ //檢查集合是否為空 Preconditions.checkNotNull(collection); Map errors = null; Iterator iterator = collection.iterator(); //當集合中沒有數據,返回空集合 if (!iterator.hasNext()){ return Collections.emptyMap(); } //遍歷集合 while (iterator.hasNext()){ Object object = iterator.next(); //Collection集合中每個對象的效驗信息 賦值給Map errors = validate(object, new Class[0]); } return errors; } }
