添加依赖

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>

注解

注解及意思

注解 功能
@AssertFalse 必须是false
@AssertTrue 必须是true
@DecimalMax 小于等于给定的值
@DecimalMin 大于等于给定的值
@Digits 可设定最大整数位数和最大小数位数
@Email 必须为一个邮箱
@Future 必须是将来的时间
@FutureOrPresent 当前或将来时间
@Max 最大值
@Min 最小值
@Negative 负数(不包括0)
@NegativeOrZero 负数或0
@NotBlank 必须包含一个非空字符,不能是空字符串
@NotEmpty 不能为空,可以为空字符串
@NotNull 不为null
@Null 为null
@Past 必须是过去的时间
@PastOrPresent 必须是过去的时间,包含现在
@Pattern 匹配正则表达式
@Positive 正数
@PositiveOrZero 正数或0
@Size 校验容器的元素个数

注解通用属性及意思

1
2
3
4
5
# message:自定义消息
String message() default "{javax.validation.constraints.Null.message}";

# groups:在进行分组校验时候使用
Class<?>[] groups() default { };

基本使用

实体类

image-20221116105646638

再属性上面添加字段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Data
@ApiModel("用户信息")
public class UserEntity {

@ApiModelProperty("用户id")
private Integer id;

@NotEmpty
@ApiModelProperty("用户名")
private String username;

@NotBlank
@ApiModelProperty("用户密码")
private String password;
}

控制类

image-20221116105739386

在控制类接收参数上面添加@Valid注解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Api(tags = "用户信息")
@RestController
@RequestMapping("user")
public class UserController {

@Resource
private UserService userService;

/**
* 添加数据
*
* @param userEntity 数据
*/
@ApiOperation(value = "添加信息", httpMethod = "POST", response = ResponseUtils.class, produces = "application/json")
@PostMapping
public ResponseUtils<Boolean> add(@Valid @RequestBody UserEntity userEntity) {
Boolean isSuccess = userService.add(userEntity);
return new ResponseUtils<Boolean>().success(isSuccess ? "添加成功" : "添加失败", isSuccess);
}
}

测试

测试数据

1
2
3
4
{ 
"password": "",
"username": ""
}

测试结果

1
2
3
4
5
{
"code": 400,
"msg": "Validation failed for argument [0] in public com.xiaofei.validation.utils.ResponseUtils<java.lang.Boolean> com.xiaofei.validation.controller.UserController.add(com.xiaofei.validation.entity.UserEntity) with 2 errors: [Field error in object 'userEntity' on field 'username': rejected value []; codes [NotEmpty.userEntity.username,NotEmpty.username,NotEmpty.java.lang.String,NotEmpty]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [userEntity.username,username]; arguments []; default message [username]]; default message [不能为空]] [Field error in object 'userEntity' on field 'password': rejected value []; codes [NotBlank.userEntity.password,NotBlank.password,NotBlank.java.lang.String,NotBlank]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [userEntity.password,password]; arguments []; default message [password]]; default message [不能为空]] ",
"data": null
}

统一返回结果

由于上面的测试结果展示的数据不方便调试,由此可以统一返回测试结果,方便调试。测试未通过异常类型MethodArgumentNotValidException

统一返回异常处理类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@RestControllerAdvice
public class GlobalExceptionHandler {

private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);

/**
* 数据校验异常
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseUtils<Object> validException(MethodArgumentNotValidException e, HttpServletRequest request) {
String requestURI = request.getRequestURI();
log.error("\n请求地址:{}\n发生系统异常\n数据校验出现问题:{}\n异常类型:{}", requestURI, e.getMessage(), e.getClass());

//封装返回数据
Map<String, String> resultMap = new HashMap<>();
BindingResult bindingResult = e.getBindingResult();
bindingResult.getFieldErrors().forEach(fieldError -> {
//获取校验未通过的字段和校验未通过的信息
resultMap.put(fieldError.getField(), fieldError.getDefaultMessage());
});

// error(Integer code, String msg, T data)
return new ResponseUtils<Object>().error(400, "数据校验出现问题", resultMap);
}
}

测试

测试数据

1
2
3
4
{ 
"password": "",
"username": ""
}

测试结果

1
2
3
4
5
6
7
8
{
"code": 400,
"msg": "数据校验出现问题",
"data": {
"password": "不能为空",
"username": "不能为空"
}
}

分组校验

当不同请求进行不同校验时,可以使用分组校验进行区分。当新增时,可以指定哪些校验生效。当修改时,可以指定哪些校验生效。实体类上校验注解,设置groups,在控制类上面@Valid改为@Validated注解,并设置其value值

添加分组接口类

接口内容为空

image-20221116105236920

image-20221116105311928

在实体类属性上添加分组

image-20221116105422477

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Data
@ApiModel("用户信息")
@TableName(value = "user")
public class UserEntity implements Serializable {

@ApiModelProperty("用户id")
@Null(message = "新增id必须为空", groups = {AddGroup.class})
private Integer id;

@NotEmpty(message = "用户名不能为空", groups = {AddGroup.class, UpdateGroup.class})
@ApiModelProperty("用户名")
private String username;

@NotBlank
@ApiModelProperty("用户密码")
private String password;

@TableField(exist = false)
private static final long serialVersionUID = 1L;
}

在控制类上指定注解

当使用@Validated指定了分组校验后,未指定分组的校验注解将不会生效

image-20221116105949762

1
2
3
4
5
6
@ApiOperation(value = "添加信息", httpMethod = "POST", response = ResponseUtils.class, produces = "application/json")
@PostMapping
public ResponseUtils<Boolean> add(@Validated(value = {AddGroup.class}) @RequestBody UserEntity userEntity) {
Boolean isSuccess = userService.add(userEntity);
return new ResponseUtils<Boolean>().success(isSuccess ? "添加成功" : "添加失败", isSuccess);
}

测试

image-20221116110255957

image-20221116110353341

自定义校验注解

image-20221116150736341

  1. 编写一个自定义的校验注解
  2. 编写一个自定义的校验器
  3. 关联自定义的校验器和自定义的校验注解

添加依赖

如果使用了spring-boot-starter-validation则不需要添加下面依赖,spring-boot-starter-validation自带该依赖

1
2
3
4
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
</dependency>

自定义校验注解

image-20221116150814837

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Documented
@Constraint(validatedBy = {ListValueConstraintValidator.class})//指定校验器
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
@Retention(RUNTIME)
public @interface ListValue {

String message() default "必须为指定的值";

Class<?>[] groups() default {};

Class<? extends Payload>[] payload() default {};

int[] values() default {};
}

编写校验器

编写一个类,实现ConstraintValidator接口,实现initializeisValid方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
/**
* 自定义校验器
* User: 李飞
* Date: 2022/11/16
* Email: 1903078434@qq.com
*/
public class ListValueConstraintValidator implements ConstraintValidator<ListValue, Integer> {

private final Set<Integer> set = new HashSet<>();

/**
* 往自定义校验注解中添加值
*
* @param listValue 自定义校验注解
*/
@Override
public void initialize(ListValue listValue) {
// 说明给定的值 @ListValue(values={0,1}) values声明的值
int[] values = listValue.values();
for (int value : values) {
//声明的值存入set
set.add(value);
}
}

/**
* 设置自定义校验规则
* 判断是否校验成功
* value:提交过来需要校验的值(传的参)
*/
@Override
public boolean isValid(Integer value, ConstraintValidatorContext constraintValidatorContext) {
//判断提交的值是否在set中
return set.contains(value);
}
}

使用

实体类

image-20221116151016604

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@Data
@ApiModel("用户信息")
@TableName(value = "user")
public class UserEntity implements Serializable {

@ApiModelProperty("用户id")
@Null(message = "新增id必须为空", groups = {AddGroup.class})
private Integer id;

@NotEmpty(message = "用户名不能为空", groups = {AddGroup.class, UpdateGroup.class})
@ApiModelProperty("用户名")
private String username;

@NotBlank
@ApiModelProperty("用户密码")
private String password;

@ListValue(values = {1, 2})
@ApiModelProperty("状态")
@TableField(exist = false)
private Integer status;

@TableField(exist = false)
private static final long serialVersionUID = 1L;
}

控制类

image-20221116151113100

1
2
3
4
5
6
@ApiOperation(value = "添加信息", httpMethod = "POST", response = ResponseUtils.class, produces = "application/json")
@PostMapping
public ResponseUtils<Boolean> add(@Validated @RequestBody UserEntity userEntity) {
Boolean isSuccess = userService.add(userEntity);
return new ResponseUtils<Boolean>().success(isSuccess ? "添加成功" : "添加失败", isSuccess);
}

常用的JSR303校验

下面的模板都是基于分组校验实现的,如果不使用分组校验,直接删除分组校验部分即可,下列可能不准确,参考即可

IP

1
2
3
@Pattern(regexp = "((\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])\\.){3}(\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])(?::(?:[0-9]|[1-9][0-9]{1,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5]))?",
message = "IP格式错误",
groups = {AddGroup.class, UpdateGroup.class})

端口

1
2
@Min(value = 1, message = "端口格式错误,必须为1-65535", groups = {AddGroup.class, UpdateGroup.class})
@Max(value = 65535, message = "端口格式错误,必须为1-65535", groups = {AddGroup.class, UpdateGroup.class})

其他

1
2
@Null(message = "新增时不能携带id", groups = {AddGroup.class})
@NotBlank(message = "修改时必须携带id", groups = {UpdateGroup.class})