- Session 认证和 Token 认证
- 过滤器和拦截器
- 基于Token认证的登录功能实现
- SpringBoot统一返回和统一异常处理
- SpringBoot项目logback日志配置
- Spring事务
- Spring AOP
- 常用注解
- SpringBoot参数校验
在日常项目开发中,我们都知道参数验证是必不可少的一环,但是有时候为了偷懒,把参数校验交给前端开发人员去处理,这样很容易影响系统稳定性和安全性,毕竟现在有很多手段可以绕过前端,直接后端请求接口。
本文就来介绍一下在
SpringBoot
应用中怎么进行参数校验。
一、使用参数校验注解
在
SpringBoot
项目中可以引用
spring-boot-starter-validation
实现数据验证。
spring-boot-starter-validation
不仅支持
JSR-303(Bean Validation 1.0)
规范,还提供了对
JSR-380(Bean Validation 2.0)
规范的全面支持。可以利用
Bean Validation 2.0
的新特性,更灵活地定义验证规则,包括对集合、嵌套对象的验证等。
<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>
通常在实体类上的字段上使用标准的
Bean Validation
注解,以下是一些常用的参数校验注解以及相关例子。
注解名称
功能
@Null
检查该字段为空
@NotNull
不能为null
@NotBlank
不能为空,常用于检查空字符串
@NotEmpty
不能为空,多用于检测list是否size是0
@Max
该字段的值只能小于或等于该值
@Min
该字段的值只能大于或等于该值
@Past
检查该字段的日期是在过去
@Future
检查该字段的日期是否是属于将来的日期
检查是否是一个有效的email地址
@Pattern(regex=,flag=)
被注释的元素必须符合指定的正则表达式
@Range(min=,max=,message=)
被注释的元素必须在合适的范围内
@Size(min=, max=)
检查该字段的size是否在min和max之间,可以是字符串、数组、集合、Map等
@Length(min=,max=)
检查所属的字段的长度是否在min和max之间,只能用于字符串
@AssertTrue
用于boolean字段,该字段只能为true
@AssertFalse
该字段的值只能为false
1.1、基本用法
1.@NotNull:校验元素值不能为空,如果为空,则校验失败。
@NotNull(message ="名字不能为空")privateString userName;
2.@NotBlank:校验字符串值不能为null和空字符串,必须包含至少一个非空字符即执行
trim
(之后不为’‘)。如果元素为
null
或者’',则验证失败。
@NotBlank(message ="昵称不能为null和空字符串")privateString nickName;
3.@NotEmpty:校验集合或者数组或者字符串是否非空,通常用于集合和数组字段,需要集合和数组元素个数大于
0
。也可以作用于字符串,此时校验字符串不能为
null
或空串(可以是一个空格)。
@NotEmpty(message ="postIds不能为空")privateLong[] postIds;
4.@Max:校验数字元素最大值。
@Max(value=100,message ="年龄最大100")privateString age;
5.@Min:校验数字元素最小值。
@Min(value=18,message ="年龄最小100")privateString age;
6.@Past:校验日期或时间元素是否在当前时间之前。即是否是过去时间。作用于
Date
相关类型的字段。
@Past(message ="")privateDate createTime;
7.@Future:校验日期或时间元素是否在当前时间之前。即是否是过去时间。作用于
Date
相关类型的字段。
@Future(message ="")privateDate createTime;
8.@Email:校验字符串元素是否为有效的电子邮件地址。
@Email(message ="")privateString email;
9.@Pattern:根据正则表达式校验字符串元素的格式。
@Pattern(regexp ="[a-zA-Z0-9]+")privateString userName;
10.@Size:校验集合元素个数或字符串的长度在指定范围内。
@Size(min =3, max =10, message ="长度在3到10之间")privateString username;
11.@Length:校验字符串元素的长度。作用于字符串。
@Length(min =3, max =10, message ="长度在3到10之间")privateString username;
以上只是部分注解和他们的功能,需要详细的了解需要查看源码。
1.2、用法示例
定义入参请求参数
packagecom.duan.pojo.vo;importcom.baomidou.mybatisplus.annotation.TableField;importio.swagger.annotations.ApiModelProperty;importlombok.Data;importjavax.validation.constraints.Email;importjavax.validation.constraints.NotBlank;/**
* @author db
* @version 1.0
* @description SysUserVO
* @since 2024/6/17
*/@DatapublicclassSysUserVO{@ApiModelProperty("部门ID")privateLong deptId;@NotBlank(message ="名字不能为空")@ApiModelProperty("用户名")privateString userName;@NotBlank(message ="昵称不能为null和空字符串")@ApiModelProperty("昵称")privateString nickName;@ApiModelProperty("密码")privateString password;@ApiModelProperty("用户性别(0男,1女")privateInteger gender;@ApiModelProperty("手机号码")privateString phone;@Email(message ="请填写正确的邮箱地址")@ApiModelProperty("邮箱")privateString email;@ApiModelProperty("头像地址")privateString avatarName;@ApiModelProperty("用户类型(0管理员,1普通用户")privateInteger userType;@ApiModelProperty("状态:1启用、0禁用")privateInteger status;@ApiModelProperty("备注")privateString remark;}
定义
mapper
packagecom.duan.mapper;importcom.baomidou.mybatisplus.core.mapper.BaseMapper;importcom.duan.pojo.SysUser;importorg.apache.ibatis.annotations.Mapper;@MapperpublicinterfaceUserMapperextendsBaseMapper<SysUser>{}
定义接口
packagecom.duan.service;importcom.baomidou.mybatisplus.extension.service.IService;importcom.duan.pojo.SysUser;publicinterfaceUserServiceextendsIService<SysUser>{voidAddUser(SysUserVO sysUserVO);}
定义接口实现
packagecom.duan.service.impl;importcom.baomidou.mybatisplus.extension.service.impl.ServiceImpl;importcom.duan.mapper.UserMapper;importcom.duan.pojo.SysUser;importcom.duan.service.UserService;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.stereotype.Service;/**
* @author db
* @version 1.0
* @description UserServiceImpl
* @since 2024/4/15
*/@ServicepublicclassUserServiceImplextendsServiceImpl<UserMapper,SysUser>implementsUserService{@AutowiredprivateUserMapper userMapper;@OverridepublicvoidAddUser(SysUserVO sysUserVO){SysUser sysUser =newSysUser();BeanUtils.copyProperties(sysUserVO,sysUser);
userMapper.insert(sysUser);}}
定义
controller
packagecom.duan.controller;importcom.duan.pojo.ResponseResult;importcom.duan.pojo.Result;importcom.duan.pojo.SysUser;importcom.duan.service.UserService;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.validation.annotation.Validated;importorg.springframework.web.bind.annotation.PostMapping;importorg.springframework.web.bind.annotation.RequestBody;importorg.springframework.web.bind.annotation.RequestMapping;importorg.springframework.web.bind.annotation.RestController;/**
* @author db
* @version 1.0
* @description UserController
* @since 2024/4/15
*/@RestController@RequestMapping("/user")publicclassUserController{@AutowiredprivateUserService userService;@PostMapping("/addUser")publicResponseResultaddUser(@RequestBody@ValidatedSysUserVO sysUserVO){userService.AddUser(sysUserVO);returnResponseResult.okResult();}}
1.3、示例测试
调用增加用户接口
注意:我们需要捕获一下
MethodArgumentNotValidException
,才能如上图显示的那样。
1.4、嵌套对象的验证
在
SysUserVO
中增加一个
address
的校验,即需要对嵌套对象进行校验。
packagecom.duan.pojo.vo;importlombok.Data;importjavax.validation.constraints.NotBlank;/**
* @author db
* @version 1.0
* @description AddressVO
* @since 2024/6/17
*/@DatapublicclassAddressVO{@NotBlank(message ="省份不能为空")privateString province;@NotBlank(message ="城市不能为空")privateString city;}
AddressVO
对象如下所示:
packagecom.duan.pojo.vo;importlombok.Data;importjavax.validation.constraints.NotBlank;/**
* @author db
* @version 1.0
* @description AddressVO
* @since 2024/6/17
*/@DatapublicclassAddressVO{@NotBlank(message ="省份不能为空")privateString province;@NotBlank(message ="城市不能为空")privateString city;}
测试
说明:为了能够进行嵌套对象验证,必须手动在
SysUserVO
实体的
addressVo
字段上明确指出这个字段里面的实体需要验证,由于
@Vaildated
不能作用在成员属性上,而且
@Valid
能加在成员属性上,同时配合
controller
中在方法参数上
@Validated
或
@Valid
来进行嵌套验证。
这里必须要说明一下
@Validated
和
@Valid
的区别
- 来源
- @Validated:Spring 框架特有的注解,是标准 JSR-303 的一个变种,提供了一个分组功能
- @Valid:标准 JSR-303 规范的标记型注解。
- 注解位置
- @Validated:作用在类上、方法上、方法参数上,不能作用于成员属性上。
- @Valid:方法、构造函数、方法参数、成员属性上。
- 分组
- @Validated:支持分组验证。
- @Valid:支持标准的 Bean 验证功能,不支持分组验证。
- 嵌套验证
- @Validated:不支持嵌套验证。
- @Valid:支持嵌套验证。
二、分组验证
同一个应用中,会出现不同的场景,比如:用户创建、用户更新、用户删除,针对不同的场景,有些字段在一个场景中需要验证,但是在另一个场景中该字段就不需要验证,我们可以选择新建不同的实体类去解决这类问题,比如:用户创建
UserCreateVO
、用户更新
UserUpdate
等,但是这样的做法会造成类的膨胀、代码的冗余。其实我们可以使用分组校验有选择的执行特定组的参数校验。定义分组校验需要注意两点:
- 定义分组必须使用接口。
- 要校验的字段必须加上分组,分组只对指定分组生效,不加分组不校验。
2.1、创建分组
创建两个分组接口,标识不同的业务场景CreateGroup用于创建时指定的分组
packagecom.duan.validatedGroup;/**
* @author db
* @version 1.0
* @description CreateUserGroup
* @since 2024/6/24
*/publicinterfaceCreateUserGroup{}
UpdateGroup
用于更新时指定的分组
packagecom.duan.validatedGroup;/**
* @author db
* @version 1.0
* @description UpdateUserGroup
* @since 2024/6/24
*/publicinterfaceUpdateUserGroup{}
2.2、使用分组校验
分组校验是通过在验证注解上指定
groups
属性来实现的。这个属性允许你为验证规则分配一个或多个验证组。假设用户创建时不传递用户
ID
,其余的参数必传,用户更新接口必须传递用户
ID
,可以不传递用户名,其他参数必须传递。
packagecom.duan.pojo.vo;importcom.baomidou.mybatisplus.annotation.TableField;importcom.duan.validatedGroup.CreateUserGroup;importcom.duan.validatedGroup.UpdateUserGroup;importio.swagger.annotations.ApiModelProperty;importlombok.Data;importjavax.validation.Valid;importjavax.validation.constraints.Email;importjavax.validation.constraints.NotBlank;importjavax.validation.constraints.NotNull;importjava.util.List;/**
* @author db
* @version 1.0
* @description SysUserVO
* @since 2024/6/17
*/@DatapublicclassSysUserVO{@NotBlank(message ="请选择用户",groups =UpdateUserGroup.class)privateLong id;@ApiModelProperty("部门ID")privateLong deptId;@NotBlank(message ="名字不能为空",groups =CreateUserGroup.class)@ApiModelProperty("用户名")privateString userName;@NotBlank(message ="昵称不能为null和空字符串")@ApiModelProperty("昵称")privateString nickName;@ApiModelProperty("密码")privateString password;@ApiModelProperty("用户性别(0男,1女")privateInteger gender;@ApiModelProperty("手机号码")privateString phone;@Email(message ="请填写正确的邮箱地址")@ApiModelProperty("邮箱")privateString email;@ApiModelProperty("头像地址")privateString avatarName;@ApiModelProperty("用户类型(0管理员,1普通用户")privateInteger userType;@ApiModelProperty("状态:1启用、0禁用")privateInteger status;@ApiModelProperty("备注")privateString remark;@NotNull(message ="请输入地址信息")@ValidprivateAddressVO addressVO ;}
2.3、在Controller中使用分组
使用
@Validated
注解,并指定要执行的验证。
packagecom.duan.controller;importcom.duan.pojo.ResponseResult;importcom.duan.pojo.Result;importcom.duan.pojo.SysUser;importcom.duan.pojo.vo.SysUserVO;importcom.duan.service.UserService;importcom.duan.validatedGroup.CreateUserGroup;importcom.duan.validatedGroup.UpdateUserGroup;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.validation.annotation.Validated;importorg.springframework.web.bind.annotation.PostMapping;importorg.springframework.web.bind.annotation.RequestBody;importorg.springframework.web.bind.annotation.RequestMapping;importorg.springframework.web.bind.annotation.RestController;/**
* @author db
* @version 1.0
* @description UserController
* @since 2024/4/15
*/@RestController@RequestMapping("/user")publicclassUserController{@AutowiredprivateUserService userService;@PostMapping("/addUser")publicResponseResultaddUser(@RequestBody@Validated(value =CreateUserGroup.class)SysUserVO sysUserVO){
userService.addUser(sysUserVO);returnResponseResult.okResult();}@PostMapping("/updateUser")publicResponseResultupdateUser(@RequestBody@Validated(value =UpdateUserGroup.class)SysUserVO sysUserVO){
userService.updateUser(sysUserVO);returnResponseResult.okResult();}}
2.4、测试
- 创建用户接口
username 不传值,即不满足 username 不能为空的条件,应该校验不通过。通过测试发现,会提示username不能为空。
- 更新用户update接口
id写成0,
username
还是为空,只是报了
id
不能小于1
三、自定义验证注解
在项目开发中,我们也经常使用自定义注解去完成字段校验。自定义校验注解步骤如下:
- 编写一个自定义校验注解
- 编写一个自定义的校验器
- 关联自定义的校验器和自定义的校验注解
假如:
user
实体中的
password
字段,格式是大于八位且包含数字大写字母小写字母,这个自定义校验怎么实现呢?
1、首先定义一个注解
PasswordValid
packagecom.duan.anno;importcom.duan.config.PasswordValidValidator;importjavax.validation.Constraint;importjavax.validation.Payload;importjava.lang.annotation.Retention;importjava.lang.annotation.Target;importstaticjava.lang.annotation.ElementType.*;importstaticjava.lang.annotation.ElementType.TYPE_USE;importstaticjava.lang.annotation.RetentionPolicy.RUNTIME;@Constraint(validatedBy ={PasswordValidValidator.class})@Target({METHOD,FIELD,ANNOTATION_TYPE,CONSTRUCTOR,PARAMETER,TYPE_USE})@Retention(RUNTIME)public@interfacePasswordValid{Stringmessage()default"{密码格式不对}";Class<?>[]groups()default{};Class<?extendsPayload>[]payload()default{};}
2、定义一个校验器
PasswordValidValidator
自定义校验器需要实现
ConstraintValidator<A extends Annotation, T>
这个接口,第一个泛型是校验注解,第二个是参数类型。
packagecom.duan.config;importcom.duan.anno.PasswordValid;importjavax.validation.ConstraintValidator;importjavax.validation.ConstraintValidatorContext;/**
* @author db
* @version 1.0
* @description PasswordValidValidator
* @since 2024/6/25
*/publicclassPasswordValidValidatorimplementsConstraintValidator<PasswordValid,String>{@Overridepublicvoidinitialize(PasswordValid constraintAnnotation){}@OverridepublicbooleanisValid(String value,ConstraintValidatorContext context){if(value ==null){returnfalse;}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、关联自定义的校验器和自定义的校验注解
当你使用
@Constraint(validatedBy = EnumValidator.class)
注解时,
Java Bean Validation
的实现框架会自动发现并注册相应的验证器。
@Constraint(validatedBy = { PasswordValidValidator.class})
在
SysUserVO
中使用
@PasswordValid(groups = CreateUserGroup.class)
@ApiModelProperty("密码")
private String password;
模拟输入
password
为纯数字时,校验不通过。
代码地址:
https://gitee.com/duan138/practice-code/tree/master/paramValidated
四、总结
本文我们了解和实践在
SpringBoot
项目中,怎么去使用基本的校验注解、嵌套校验、分组校验,同时又学习了怎么使用自定义校验注解,在项目中合理地使用相关校验注解,可以简化代码、提高代码可读性和可维护性。
改变你能改变的,接受你不能改变的,关注公众号:程序员康康,一起成长,共同进步。
版权归原作者 cxykk1217 所有, 如有侵权,请联系我们删除。