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í)顚?xiě)正確的郵箱地址") @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í)顚?xiě)正確的郵箱地址") @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寫(xiě)成0,username
還是為空,只是報(bào)了id
不能小于1
三、自定義驗(yàn)證注解
在項(xiàng)目開(kāi)發(fā)中,我們也經(jīng)常使用自定義注解去完成字段校驗(yàn)。自定義校驗(yàn)注解步驟如下:
- 編寫(xiě)一個(gè)自定義校驗(yàn)注解
- 編寫(xiě)一個(gè)自定義的校驗(yàn)器
- 關(guān)聯(lián)自定義的校驗(yàn)器和自定義的校驗(yàn)注解
假如:user
實(shí)體中的password
字段,格式是大于八位且包含數(shù)字大寫(xiě)字母小寫(xiě)字母,這個(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-12Maven項(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-11Java編程中應(yīng)用的GUI設(shè)計(jì)基礎(chǔ)
這篇文章主要介紹了Java編程中應(yīng)用的GUI設(shè)計(jì)基礎(chǔ),為一些Java開(kāi)發(fā)CS類型應(yīng)用的基礎(chǔ)概念知識(shí),需要的朋友可以參考下2015-10-10java中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