前言
數據效驗工作在開發工作中,是非常重要的,保證數據的正確性,可靠性,安全性。不僅在前端進行效驗,還要在後臺繼續進行效驗。
前端做驗證只是爲了用戶體驗,比如控制按鈕的顯示隱藏,單頁應用的路由跳轉等等。後端纔是最終的保障。總之,一切用戶的輸入都是不可信的。
常見的驗證方式
前端的校驗是必須的,這個很簡單,因爲客戶體驗。後臺的校驗更是必須的,關鍵在於如何與目前我們的分層思想(控制層、業務層、持久層)綜合起來考慮。
在每層都要進行校驗嗎?還是隻在是某個特定層做就可以了? 是否有好的校驗框架(如前端的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;
}
}