SpringBoot參數(shù)校驗(yàn)的一些實(shí)戰(zhàn)應(yīng)用

在日常項(xiàng)目開(kāi)發(fā)中,我們都知道參數(shù)驗(yàn)證是必不可少的一環(huán),但是有時(shí)候?yàn)榱送祽校褏?shù)校驗(yàn)交給前端開(kāi)發(fā)人員去處理,這樣很容易影響系統(tǒng)穩(wěn)定性和安全性,畢竟現(xiàn)在有很多手段可以繞過(guò)前端,直接后端請(qǐng)求接口。
本文就來(lái)介紹一下在 SpringBoot 應(yīng)用中怎么進(jìn)行參數(shù)校驗(yàn)。
一、使用參數(shù)校驗(yàn)注解
在 SpringBoot 項(xiàng)目中可以引用 spring-boot-starter-validation 實(shí)現(xiàn)數(shù)據(jù)驗(yàn)證。spring-boot-starter-validation 不僅支持 JSR-303(Bean Validation 1.0)規(guī)范,還提供了對(duì) JSR-380(Bean Validation 2.0)規(guī)范的全面支持??梢岳?nbsp;Bean Validation 2.0 的新特性,更靈活地定義驗(yàn)證規(guī)則,包括對(duì)集合、嵌套對(duì)象的驗(yàn)證等。
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> <version>2.4.3</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency>
通常在實(shí)體類上的字段上使用標(biāo)準(zhǔn)的 Bean Validation 注解,以下是一些常用的參數(shù)校驗(yàn)注解以及相關(guān)例子。
注解名稱 | 功能 |
@Null | 檢查該字段為空 |
@NotNull | 不能為null |
@NotBlank | 不能為空,常用于檢查空字符串 |
@NotEmpty | 不能為空,多用于檢測(cè)list是否size是0 |
@Max | 該字段的值只能小于或等于該值 |
@Min | 該字段的值只能大于或等于該值 |
@Past | 檢查該字段的日期是在過(guò)去 |
@Future | 檢查該字段的日期是否是屬于將來(lái)的日期 |
檢查是否是一個(gè)有效的email地址 | |
@Pattern(regex=,flag=) | 被注釋的元素必須符合指定的正則表達(dá)式 |
@Range(min=,max=,message=) | 被注釋的元素必須在合適的范圍內(nèi) |
@Size(min=, max=) | 檢查該字段的size是否在min和max之間,可以是字符串、數(shù)組、集合、Map等 |
@Length(min=,max=) | 檢查所屬的字段的長(zhǎng)度是否在min和max之間,只能用于字符串 |
@AssertTrue | 用于boolean字段,該字段只能為true |
@AssertFalse | 該字段的值只能為false |
1.1、基本用法
1.@NotNull:校驗(yàn)元素值不能為空,如果為空,則校驗(yàn)失敗。
@NotNull(message = "名字不能為空") private String userName;
2.@NotBlank:校驗(yàn)字符串值不能為null和空字符串,必須包含至少一個(gè)非空字符即執(zhí)行trim(之后不為’‘)。如果元素為null或者’',則驗(yàn)證失敗。
@NotBlank(message = "昵稱不能為null和空字符串")
private String nickName;
3.@NotEmpty:校驗(yàn)集合或者數(shù)組或者字符串是否非空,通常用于集合和數(shù)組字段,需要集合和數(shù)組元素個(gè)數(shù)大于0。也可以作用于字符串,此時(shí)校驗(yàn)字符串不能為null或空串(可以是一個(gè)空格)。
@NotEmpty(message = "postIds不能為空")
private Long[] postIds;
4.@Max:校驗(yàn)數(shù)字元素最大值。
@Max(value=100,message = "年齡最大100")
private String age;
5.@Min:校驗(yàn)數(shù)字元素最小值。
@Min(value=18,message = "年齡最小100")
private String age;
6.@Past:校驗(yàn)日期或時(shí)間元素是否在當(dāng)前時(shí)間之前。即是否是過(guò)去時(shí)間。作用于Date相關(guān)類型的字段。
@Past(message = "")
private Date createTime;
7.@Future:校驗(yàn)日期或時(shí)間元素是否在當(dāng)前時(shí)間之前。即是否是過(guò)去時(shí)間。作用于Date相關(guān)類型的字段。
@Future(message = "")
private Date createTime;
8.@Email:校驗(yàn)字符串元素是否為有效的電子郵件地址。
@Email(message = "")
private String email;
9.@Pattern:根據(jù)正則表達(dá)式校驗(yàn)字符串元素的格式。
@Pattern(regexp = "[a-zA-Z0-9]+")
private String userName;
10.@Size:校驗(yàn)集合元素個(gè)數(shù)或字符串的長(zhǎng)度在指定范圍內(nèi)。
@Size(min = 3, max = 10, message = "長(zhǎng)度在3到10之間") private String username;
11.@Length:校驗(yàn)字符串元素的長(zhǎng)度。作用于字符串。
@Length(min = 3, max = 10, message = "長(zhǎng)度在3到10之間") private String username;
以上只是部分注解和他們的功能,需要詳細(xì)的了解需要查看源碼。
1.2、用法示例
定義入?yún)⒄?qǐng)求參數(shù)
package com.duan.pojo.vo;
import com.baomidou.mybatisplus.annotation.TableField;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
/**
* @author db
* @version 1.0
* @description SysUserVO
* @since 2024/6/17
*/
@Data
public class SysUserVO {
@ApiModelProperty("部門ID")
private Long deptId;
@NotBlank(message = "名字不能為空")
@ApiModelProperty("用戶名")
private String userName;
@NotBlank(message = "昵稱不能為null和空字符串")
@ApiModelProperty("昵稱")
private String nickName;
@ApiModelProperty("密碼")
private String password;
@ApiModelProperty("用戶性別(0男,1女")
private Integer gender;
@ApiModelProperty("手機(jī)號(hào)碼")
private String phone;
@Email(message = "請(qǐng)?zhí)顚懻_的郵箱地址")
@ApiModelProperty("郵箱")
private String email;
@ApiModelProperty("頭像地址")
private String avatarName;
@ApiModelProperty("用戶類型(0管理員,1普通用戶")
private Integer userType;
@ApiModelProperty("狀態(tài):1啟用、0禁用")
private Integer status;
@ApiModelProperty("備注")
private String remark;
}
定義mapper
package com.duan.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.duan.pojo.SysUser;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface UserMapper extends BaseMapper<SysUser> {
}
定義接口
package com.duan.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.duan.pojo.SysUser;
public interface UserService extends IService<SysUser> {
void AddUser(SysUserVO sysUserVO);
}
定義接口實(shí)現(xiàn)
package com.duan.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.duan.mapper.UserMapper;
import com.duan.pojo.SysUser;
import com.duan.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* @author db
* @version 1.0
* @description UserServiceImpl
* @since 2024/4/15
*/
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, SysUser> implements UserService {
@Autowired
private UserMapper userMapper;
@Override
public void AddUser(SysUserVO sysUserVO) {
SysUser sysUser = new SysUser();
BeanUtils.copyProperties(sysUserVO,sysUser);
userMapper.insert(sysUser);
}
}
定義controller
package com.duan.controller;
import com.duan.pojo.ResponseResult;
import com.duan.pojo.Result;
import com.duan.pojo.SysUser;
import com.duan.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author db
* @version 1.0
* @description UserController
* @since 2024/4/15
*/
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@PostMapping("/addUser")
public ResponseResult addUser(@RequestBody @Validated SysUserVO sysUserVO){
userService.AddUser(sysUserVO);
return ResponseResult.okResult();
}
}
1.3、示例測(cè)試
調(diào)用增加用戶接口

注意:我們需要捕獲一下 MethodArgumentNotValidException,才能如上圖顯示的那樣。
1.4、嵌套對(duì)象的驗(yàn)證
在SysUserVO中增加一個(gè)address的校驗(yàn),即需要對(duì)嵌套對(duì)象進(jìn)行校驗(yàn)。
package com.duan.pojo.vo;
import lombok.Data;
import javax.validation.constraints.NotBlank;
/**
* @author db
* @version 1.0
* @description AddressVO
* @since 2024/6/17
*/
@Data
public class AddressVO {
@NotBlank(message = "省份不能為空")
private String province;
@NotBlank(message = "城市不能為空")
private String city;
}
AddressVO 對(duì)象如下所示:
package com.duan.pojo.vo;
import lombok.Data;
import javax.validation.constraints.NotBlank;
/**
* @author db
* @version 1.0
* @description AddressVO
* @since 2024/6/17
*/
@Data
public class AddressVO {
@NotBlank(message = "省份不能為空")
private String province;
@NotBlank(message = "城市不能為空")
private String city;
}
測(cè)試

說(shuō)明:為了能夠進(jìn)行嵌套對(duì)象驗(yàn)證,必須手動(dòng)在
SysUserVO實(shí)體的addressVo字段上明確指出這個(gè)字段里面的實(shí)體需要驗(yàn)證,由于@Vaildated不能作用在成員屬性上,而且@Valid能加在成員屬性上,同時(shí)配合controller中在方法參數(shù)上@Validated或@Valid來(lái)進(jìn)行嵌套驗(yàn)證。
這里必須要說(shuō)明一下@Validated 和@Valid 的區(qū)別
- 來(lái)源
- @Validated:Spring 框架特有的注解,是標(biāo)準(zhǔn) JSR-303 的一個(gè)變種,提供了一個(gè)分組功能
- @Valid:標(biāo)準(zhǔn) JSR-303 規(guī)范的標(biāo)記型注解。
- 注解位置
- @Validated:作用在類上、方法上、方法參數(shù)上,不能作用于成員屬性上。
- @Valid:方法、構(gòu)造函數(shù)、方法參數(shù)、成員屬性上。
- 分組
- @Validated:支持分組驗(yàn)證。
- @Valid:支持標(biāo)準(zhǔn)的 Bean 驗(yàn)證功能,不支持分組驗(yàn)證。
- 嵌套驗(yàn)證
- @Validated:不支持嵌套驗(yàn)證。
- @Valid:支持嵌套驗(yàn)證。
二、分組驗(yàn)證
同一個(gè)應(yīng)用中,會(huì)出現(xiàn)不同的場(chǎng)景,比如:用戶創(chuàng)建、用戶更新、用戶刪除,針對(duì)不同的場(chǎng)景,有些字段在一個(gè)場(chǎng)景中需要驗(yàn)證,但是在另一個(gè)場(chǎng)景中該字段就不需要驗(yàn)證,我們可以選擇新建不同的實(shí)體類去解決這類問(wèn)題,比如:用戶創(chuàng)建 UserCreateVO、用戶更新 UserUpdate 等,但是這樣的做法會(huì)造成類的膨脹、代碼的冗余。其實(shí)我們可以使用分組校驗(yàn)有選擇的執(zhí)行特定組的參數(shù)校驗(yàn)。定義分組校驗(yàn)需要注意兩點(diǎn):
- 定義分組必須使用接口。
- 要校驗(yàn)的字段必須加上分組,分組只對(duì)指定分組生效,不加分組不校驗(yàn)。
2.1、創(chuàng)建分組
創(chuàng)建兩個(gè)分組接口,標(biāo)識(shí)不同的業(yè)務(wù)場(chǎng)景CreateGroup用于創(chuàng)建時(shí)指定的分組
package com.duan.validatedGroup;
/**
* @author db
* @version 1.0
* @description CreateUserGroup
* @since 2024/6/24
*/
public interface CreateUserGroup {
}
UpdateGroup用于更新時(shí)指定的分組
package com.duan.validatedGroup;
/**
* @author db
* @version 1.0
* @description UpdateUserGroup
* @since 2024/6/24
*/
public interface UpdateUserGroup {
}
2.2、使用分組校驗(yàn)
分組校驗(yàn)是通過(guò)在驗(yàn)證注解上指定 groups 屬性來(lái)實(shí)現(xiàn)的。這個(gè)屬性允許你為驗(yàn)證規(guī)則分配一個(gè)或多個(gè)驗(yàn)證組。假設(shè)用戶創(chuàng)建時(shí)不傳遞用戶ID,其余的參數(shù)必傳,用戶更新接口必須傳遞用戶ID,可以不傳遞用戶名,其他參數(shù)必須傳遞。
package com.duan.pojo.vo;
import com.baomidou.mybatisplus.annotation.TableField;
import com.duan.validatedGroup.CreateUserGroup;
import com.duan.validatedGroup.UpdateUserGroup;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.Valid;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.util.List;
/**
* @author db
* @version 1.0
* @description SysUserVO
* @since 2024/6/17
*/
@Data
public class SysUserVO {
@NotBlank(message = "請(qǐng)選擇用戶",groups = UpdateUserGroup.class)
private Long id;
@ApiModelProperty("部門ID")
private Long deptId;
@NotBlank(message = "名字不能為空",groups = CreateUserGroup.class)
@ApiModelProperty("用戶名")
private String userName;
@NotBlank(message = "昵稱不能為null和空字符串")
@ApiModelProperty("昵稱")
private String nickName;
@ApiModelProperty("密碼")
private String password;
@ApiModelProperty("用戶性別(0男,1女")
private Integer gender;
@ApiModelProperty("手機(jī)號(hào)碼")
private String phone;
@Email(message = "請(qǐng)?zhí)顚懻_的郵箱地址")
@ApiModelProperty("郵箱")
private String email;
@ApiModelProperty("頭像地址")
private String avatarName;
@ApiModelProperty("用戶類型(0管理員,1普通用戶")
private Integer userType;
@ApiModelProperty("狀態(tài):1啟用、0禁用")
private Integer status;
@ApiModelProperty("備注")
private String remark;
@NotNull(message = "請(qǐng)輸入地址信息")
@Valid
private AddressVO addressVO ;
}
2.3、在Controller中使用分組
使用@Validated 注解,并指定要執(zhí)行的驗(yàn)證。
package com.duan.controller;
import com.duan.pojo.ResponseResult;
import com.duan.pojo.Result;
import com.duan.pojo.SysUser;
import com.duan.pojo.vo.SysUserVO;
import com.duan.service.UserService;
import com.duan.validatedGroup.CreateUserGroup;
import com.duan.validatedGroup.UpdateUserGroup;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author db
* @version 1.0
* @description UserController
* @since 2024/4/15
*/
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@PostMapping("/addUser")
public ResponseResult addUser(@RequestBody @Validated(value = CreateUserGroup.class) SysUserVO sysUserVO){
userService.addUser(sysUserVO);
return ResponseResult.okResult();
}
@PostMapping("/updateUser")
public ResponseResult updateUser(@RequestBody @Validated(value = UpdateUserGroup.class) SysUserVO sysUserVO){
userService.updateUser(sysUserVO);
return ResponseResult.okResult();
}
}
2.4、測(cè)試
- 創(chuàng)建用戶接口
username 不傳值,即不滿足 username 不能為空的條件,應(yīng)該校驗(yàn)不通過(guò)。通過(guò)測(cè)試發(fā)現(xiàn),會(huì)提示username不能為空。

- 更新用戶update接口
id寫成0,username還是為空,只是報(bào)了id不能小于1

三、自定義驗(yàn)證注解
在項(xiàng)目開(kāi)發(fā)中,我們也經(jīng)常使用自定義注解去完成字段校驗(yàn)。自定義校驗(yàn)注解步驟如下:
- 編寫一個(gè)自定義校驗(yàn)注解
- 編寫一個(gè)自定義的校驗(yàn)器
- 關(guān)聯(lián)自定義的校驗(yàn)器和自定義的校驗(yàn)注解
假如:user實(shí)體中的password字段,格式是大于八位且包含數(shù)字大寫字母小寫字母,這個(gè)自定義校驗(yàn)怎么實(shí)現(xiàn)呢?
1、首先定義一個(gè)注解PasswordValid
package com.duan.anno;
import com.duan.config.PasswordValidValidator;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.ElementType.TYPE_USE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Constraint(validatedBy = { PasswordValidValidator.class})
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
public @interface PasswordValid {
String message() default "{密碼格式不對(duì)}";
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default { };
}
2、定義一個(gè)校驗(yàn)器PasswordValidValidator
自定義校驗(yàn)器需要實(shí)現(xiàn) ConstraintValidator<A extends Annotation, T>這個(gè)接口,第一個(gè)泛型是校驗(yàn)注解,第二個(gè)是參數(shù)類型。
package com.duan.config;
import com.duan.anno.PasswordValid;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
/**
* @author db
* @version 1.0
* @description PasswordValidValidator
* @since 2024/6/25
*/
public class PasswordValidValidator implements ConstraintValidator<PasswordValid,String> {
@Override
public void initialize(PasswordValid constraintAnnotation) {
}
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
if (value == null) {
return false;
}
boolean hasUppercase = value.chars().anyMatch(Character::isUpperCase);
boolean hasLowercase = value.chars().anyMatch(Character::isLowerCase);
boolean hasDigit = value.chars().anyMatch(Character::isDigit);
return value.length() >= 8 && hasUppercase && hasLowercase && hasDigit;
}
}
3、關(guān)聯(lián)自定義的校驗(yàn)器和自定義的校驗(yàn)注解
當(dāng)你使用 @Constraint(validatedBy = EnumValidator.class) 注解時(shí),Java Bean Validation 的實(shí)現(xiàn)框架會(huì)自動(dòng)發(fā)現(xiàn)并注冊(cè)相應(yīng)的驗(yàn)證器。
@Constraint(validatedBy = { PasswordValidValidator.class})
在SysUserVO中使用
@PasswordValid(groups = CreateUserGroup.class)
@ApiModelProperty("密碼")
private String password;
模擬輸入password為純數(shù)字時(shí),校驗(yàn)不通過(guò)。

代碼地址:https://gitee.com/duan138/practice-code/tree/master/paramValidated
四、總結(jié)
本文我們了解和實(shí)踐在 SpringBoot 項(xiàng)目中,怎么去使用基本的校驗(yàn)注解、嵌套校驗(yàn)、分組校驗(yàn),同時(shí)又學(xué)習(xí)了怎么使用自定義校驗(yàn)注解,在項(xiàng)目中合理地使用相關(guān)校驗(yàn)注解,可以簡(jiǎn)化代碼、提高代碼可讀性和可維護(hù)性。
到此這篇關(guān)于SpringBoot參數(shù)校驗(yàn)的文章就介紹到這了,更多相關(guān)SpringBoot參數(shù)校驗(yàn)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java數(shù)據(jù)結(jié)構(gòu)關(guān)于棧的實(shí)例應(yīng)用
大家好,本篇文章主要講的是java數(shù)據(jù)結(jié)構(gòu)關(guān)于棧的實(shí)例應(yīng)用,感興趣的同學(xué)趕快來(lái)看一看吧,對(duì)你有幫助的話記得收藏一下,方便下次瀏覽2021-12-12
Maven項(xiàng)src/main/java目錄下配置文件無(wú)法被導(dǎo)出或者生效的問(wèn)題和處理方案
這篇文章主要介紹了Maven項(xiàng)src/main/java目錄下配置文件無(wú)法被導(dǎo)出或者生效的問(wèn)題和處理方案,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11
非常詳細(xì)的Java異常處理機(jī)制知識(shí)整理大全
Java異常指在程序運(yùn)行時(shí)可能出現(xiàn)的一些錯(cuò)誤,比如試圖打開(kāi)一個(gè)根本不存在的文件等,異常處理將會(huì)改變程序的控制流程,讓程序有機(jī)會(huì)對(duì)錯(cuò)誤做出處理,下面這篇文章主要給大家介紹了關(guān)于Java異常處理機(jī)制知識(shí)整理的相關(guān)資料,需要的朋友可以參考下2022-11-11
Java編程中應(yīng)用的GUI設(shè)計(jì)基礎(chǔ)
這篇文章主要介紹了Java編程中應(yīng)用的GUI設(shè)計(jì)基礎(chǔ),為一些Java開(kāi)發(fā)CS類型應(yīng)用的基礎(chǔ)概念知識(shí),需要的朋友可以參考下2015-10-10
java中Map如何根據(jù)key的大小進(jìn)行排序詳解
這篇文章主要給大家介紹了關(guān)于java中Map如何根據(jù)key的大小進(jìn)行排序的相關(guān)資料,有時(shí)候我們業(yè)務(wù)上需要對(duì)map里面的值按照key的大小來(lái)進(jìn)行排序的時(shí)候我們就可以利用如下方法來(lái)進(jìn)行排序了,需要的朋友可以參考下2023-09-09
如何使用Spring Security手動(dòng)驗(yàn)證用戶的方法示例
這篇文章主要介紹了如何使用Spring Security手動(dòng)驗(yàn)證用戶的方法示例,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2019-05-05

