SpringBoot參數(shù)校驗(yàn)的最佳實(shí)戰(zhàn)教程
前言
我們這里使用hibernate-validator作為對(duì)象參數(shù)驗(yàn)證器,所以在正式介紹SpringBoot參數(shù)驗(yàn)證之前,需要先簡(jiǎn)單了解一下hibernate-validator的使用。
hibernate-validator基本使用
引入依賴
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.0.17.Final</version>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
</dependency>
編寫需要驗(yàn)證對(duì)象
驗(yàn)證要求 person對(duì)象的用戶名不能為空,年齡在1-150歲之間。
@Data
public class Person {
@NotBlank(message = "username must not be null")
private String username;
@Min(value = 1, message = "age must be >= 1")
@Max(value = 150, message = "age must be < 150")
private Integer age;
}
驗(yàn)證對(duì)象屬性是否符合要求
/**
* 對(duì)象驗(yàn)證器
*/
public Validator validator() {
ValidatorFactory validatorFactory =
Validation
.byProvider(HibernateValidator.class)
.configure()
// 驗(yàn)證屬性時(shí),如果有一個(gè)驗(yàn)證不通過就返回,不需要驗(yàn)證所有屬性
.addProperty("hibernate.validator.fail_fast", "true")
.buildValidatorFactory();
return validatorFactory.getValidator();
}
@Test
public void test() throws Exception {
Person person = new Person();
Set<ConstraintViolation<Person>> validate = validator().validate(person);
validate.forEach(errorParam -> {
System.out.println(errorParam.getMessage());
});
}
- 我們只需要驗(yàn)證的對(duì)象實(shí)例即可完成對(duì)象驗(yàn)證,如果驗(yàn)證成功,那么返回一個(gè)空的集合,如果驗(yàn)證失敗,會(huì)返回具體的驗(yàn)證失敗的屬性信息。
- 我們輸出驗(yàn)證失敗的錯(cuò)誤信息如下:
username must not be null
驗(yàn)證規(guī)則
validator提供了大量的驗(yàn)證注解供我們使用,主要以下幾類:

空/非空驗(yàn)證
- @Null 元素必須為空
- @NotNull 元素不能為空,空字符串""是非空
以下所有驗(yàn)證規(guī)則都在元素非空的時(shí)候才會(huì)進(jìn)行驗(yàn)證,如果傳入的元素為空,驗(yàn)證都會(huì)通過。
bool
- @AssertTrue 元素必須為true
- @AssertFalse 元素必須為false
時(shí)間
- @Future 元素必須是未來的某個(gè)時(shí)間。
- @FutureOrPresent 元素必須是未來或者現(xiàn)在的某個(gè)時(shí)間。
- @Past 元素必須是過去的某個(gè)時(shí)間。
- @PastOrPresent 元素必須是過去或者現(xiàn)在的某個(gè)時(shí)間。
數(shù)學(xué)
數(shù)字類型可以是BigDecimal、BigInteger、CharSequence 、byte 、 short 、 int 、 long以及它們各自的包裝器類型
- @Digits 元素必須是該數(shù)字類型下可以被接受的數(shù)值范圍內(nèi)。
- @Negative 元素必須是負(fù)數(shù)
- @NegativeOrZero 元素必須小于等于0
- @Positive 元素必須大于0
- @PositiveOrZero 元素必須大于等于0
- @Max,@Min 元素的大小必須符合指定大小
字符串
- @Email 郵箱格式驗(yàn)證
- @NotBlack 驗(yàn)證字符串非空,空字符串""也屬于空
- @Pattern 字符串正則驗(yàn)證
模板正則
validator提供了字符串模板正則的注解,這里提供一份常用的正則表達(dá)式,大家可以直接作為常量工具類放到項(xiàng)目里使用
public interface ValidatorPattern {
/**
* 正則表達(dá)式:驗(yàn)證用戶名
* 1.長(zhǎng)度在5-17
* 2.由大寫小寫字母構(gòu)成
*/
String REGEX_USERNAME = "^[a-zA-Z]\w{5,17}$";
/**
* 正則表達(dá)式:驗(yàn)證密碼
* 密碼只能為 6 - 12位數(shù)字,字母及常用符號(hào)組成。
*/
String REGEX_PASSWORD = "^(?=.*[a-zA-Z])(?=.*[0-9])[A-Za-z0-9._~!@#$^&*]{6,12}$";
/**
* 正則表達(dá)式:驗(yàn)證手機(jī)號(hào)
*/
String REGEX_MOBILE = "^[1][34578]\d{9}$";
/**
* 正則表達(dá)式:驗(yàn)證郵箱
*/
String REGEX_EMAIL = "^.+@([a-z0-9A-Z]+(-[a-z0-9A-Z]+)?\.)+[a-zA-Z]{2,}$";
/**
* 正則表達(dá)式:驗(yàn)證漢字
*/
String REGEX_CHINESE = "^[\u4e00-\u9fa5],*$";
/**
* 正則表達(dá)式:驗(yàn)證身份證
*/
String REGEX_ID_CARD = "(^\d{18}$)|(^\d{15}$)";
/**
* 正則表達(dá)式:驗(yàn)證URL
*/
String REGEX_URL = "http(s)?://([\w-]+\.)+[\w-]+(/[\w- ./?%&=]*)?";
/**
* 正則表達(dá)式:驗(yàn)證IP地址
*/
String REGEX_IP_ADDR = "(25[0-5]|2[0-4]\d|[0-1]\d{2}|[1-9]?\d)";
/**
* 車牌號(hào)正則
*/
String LICENSE_NO = "^[京津滬渝冀豫云遼黑湘皖魯新蘇浙贛鄂桂甘晉蒙陜吉閩貴粵青藏川寧瓊使領(lǐng)A-Z][A-Z][A-Z0-9]{4,5}[A-Z0-9掛學(xué)警港澳]$";
/**
* 姓名校驗(yàn)
* 1~15位
* 姓名支持空格和中文的點(diǎn)
*/
String NAME = "[\u4e00-\u9fa5\u00b7\sA-Za-z]{1,15}$";
/**
* 表情正則
*/
String EMOJI = "[\ud83c\udc00-\ud83c\udfff]|[\ud83d\udc00-\ud83d\udfff]|[\u2600-\u27ff]";
/**
* 數(shù)字正則
*/
String NUMBER = "^[0-9]*$";
/**
* n位的數(shù)字
*/
String N_NUMS = "^\d{n}$";
}
SpringBoot整合hibernate-validator
引入依賴
這個(gè)不再贅述,直接拷貝上文的依賴信息
配置hibernate-validator驗(yàn)證器對(duì)象
在配置類中加入hibernate-validator驗(yàn)證器對(duì)象
@Bean
@Primary
public Validator validator() {
ValidatorFactory validatorFactory =
Validation
.byProvider(HibernateValidator.class)
.configure()
.addProperty("hibernate.validator.fail_fast", "true")
.buildValidatorFactory();
return validatorFactory.getValidator();
}
借助SpringMVC統(tǒng)一異常處理處理參數(shù)校驗(yàn)結(jié)果
配置好后,Spring會(huì)自動(dòng)幫助我們進(jìn)行參數(shù)驗(yàn)證,如果參數(shù)驗(yàn)證不通過,會(huì)拋出BindException異常,我們剛剛手動(dòng)驗(yàn)證時(shí)的Set<ConstraintViolation<Person>>通過該異常獲取。
我們這可以通過借助SpringMVC統(tǒng)一異常處理的能力處理這個(gè)異常
@Slf4j
@RestControllerAdvice
public class BaseExceptionHandler {
/**
* spring validation 自動(dòng)校驗(yàn)的參數(shù)異常
*
* @param e BindException
* @return R<Void>
*/
@ResponseStatus(org.springframework.http.HttpStatus.PAYMENT_REQUIRED)
@ExceptionHandler(BindException.class)
public R<Void> handler(BindException e) {
String defaultMsg = e.getBindingResult().getAllErrors()
.stream()
.map(ObjectError::getDefaultMessage)
.collect(Collectors.joining(":"));
log.warn(defaultMsg);
return R.of(IRespCode.PARAMETERS_ANOMALIES.getCode(), e.getMessage());
}
}
使用參數(shù)校驗(yàn)
我們只需要在校驗(yàn)參數(shù)的方法傳參上標(biāo)注@Valid或者@Validated都行
@PostMapping("register")
public R<Void> register(@Valid @RequestBody Person person) {
// todo
return R.ok();
}
分組校驗(yàn)
那么@Valid和@Validated有什么區(qū)別呢?
Validated比Valid多了一個(gè)屬性,這個(gè)屬性用于分組校驗(yàn)使用
public @interface Valid {
}
public @interface Validated {
Class<?>[] value() default {};
}
啥叫分組校驗(yàn)?
就是一個(gè)實(shí)體類中的屬性,在不同的方法傳參中,方法的對(duì)屬性的要求不同。
比如說,Person類中有三個(gè)屬性,一個(gè)是用戶名稱,一個(gè)是郵箱,一個(gè)是年齡。
在注冊(cè)用戶接口中,用戶名稱,郵箱和年齡都不能為空,但是在更改用戶的信息接口中,用戶的年齡和郵箱都可以為空,但是用戶名稱不能為空。
這時(shí)候,我們就可以按照對(duì)屬性校驗(yàn)的要求進(jìn)行分組。
新建一個(gè)RegisterGroup分組,該分組只是一個(gè)空的接口,僅僅用于標(biāo)記該校驗(yàn)要求
public interface RegisterGroup {
}
對(duì)校驗(yàn)要求進(jìn)行分組
@Data
public class Person {
@NotBlank(message = "username must not be null")
private String username;
@Min(value = 1, message = "age must be >= 1")
@Max(value = 150, message = "age must be < 150")
@NotNull(message = "age must not be null", groups = RegisterGroup.class)
private Integer age;
@Email(message = "email format error")
@NotBlank(message = "email must not be null",groups = RegisterGroup.class)
private String email;
}
方法調(diào)用時(shí),加入分組要求
@PostMapping("register")
public R<Void> register(@Validated(value = RegisterGroup.class) @RequestBody Person person) {
// todo
return R.ok();
}
這種方式其實(shí)不推薦使用,我在標(biāo)題的時(shí)候,也已經(jīng)標(biāo)記為“過時(shí)”,因?yàn)椋覀兺耆梢詾檫@兩個(gè)不同的接口創(chuàng)建兩個(gè)不同的實(shí)體類,而不是使用分組對(duì)校驗(yàn)要求進(jìn)行隔離,因?yàn)閷?shí)際生產(chǎn)環(huán)境中,分組可能有非常多個(gè),這會(huì)為我們的程序的可讀性埋下隱患,后期開發(fā)人員難以維護(hù),而且對(duì)于自動(dòng)生成API文檔也不友好。大家對(duì)于分組只需要了解即可,不建議在項(xiàng)目開發(fā)中使用。
總結(jié)
到此這篇關(guān)于SpringBoot參數(shù)校驗(yàn)的文章就介紹到這了,更多相關(guān)SpringBoot參數(shù)校驗(yàn)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
數(shù)據(jù)庫連接超時(shí)java處理的兩種方式
這篇文章主要介紹了數(shù)據(jù)庫連接超時(shí)java處理的兩種方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-04-04
解決SpringBoot項(xiàng)目啟動(dòng)成功但接口訪問404的問題
這篇文章主要介紹了如何解決SpringBoot項(xiàng)目啟動(dòng)成功但接口訪問404的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,接下來就跟著小編一起來看看吧2023-07-07
SpringBoot SSO輕松實(shí)現(xiàn)(附demo)
這篇文章主要介紹了SpringBoot SSO輕松實(shí)現(xiàn)(附demo),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-01-01
java.util.Random和concurrent.ThreadLocalRandom使用對(duì)比
這篇文章主要介紹了java.util.Random和concurrent.ThreadLocalRandom使用對(duì)比,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-07-07
基于Java實(shí)現(xiàn)無向環(huán)和有向環(huán)的檢測(cè)
這篇文章主要介紹了如何在?Java?中實(shí)現(xiàn)無向環(huán)和有向環(huán)的檢測(cè),文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)Java有一定的幫助,需要的可以參考一下2022-04-04
Java SpringBoot實(shí)現(xiàn)AOP
AOP包括連接點(diǎn)(JoinPoint)、切入點(diǎn)(Pointcut)、增強(qiáng)(Advisor)、切面(Aspect)、AOP代理(AOP Proxy),具體的方法和類型下面文章會(huì)舉例說明,感興趣的小伙伴和小編一起閱讀全文吧2021-09-09
java 線程之對(duì)象的同步和異步(實(shí)例講解)
下面小編就為大家?guī)硪黄猨ava 線程之對(duì)象的同步和異步(實(shí)例講解)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-07-07

