Spring Boot参数校验
前言
在web开发中,前端的参数校验可以提高用户的体验,后端的参数校验为了数据的安全。
在之前的开发中参数校验一般如下图

没有任何问题,只有有些不简洁不优雅
使用 hibernate-validator 来进行参数校验
示例
导入依赖
版本自由选择
1 2 3 4 5
| <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-validator</artifactId> <version>6.1.0.Final</version> </dependency>
|
常用的校验注解
- @AssertFalse 所注解的元素必须是Boolean类型,且值为false
- @AssertTrue 所注解的元素必须是Boolean类型,且值为true
- @DecimalMax 所注解的元素必须是数字,且值小于等于给定的值
- @DecimalMin 所注解的元素必须是数字,且值大于等于给定的值
- @Digits 所注解的元素必须是数字,且值必须是指定的位数
- @Future 所注解的元素必须是将来某个日期
- @Max 所注解的元素必须是数字,且值小于等于给定的值
- @Min 所注解的元素必须是数字,且值小于等于给定的值
- @Range 所注解的元素需在指定范围区间内
- @NotNull 所注解的元素值不能为null
- @NotBlank 所注解的元素值有内容
- @Null 所注解的元素值为null
- @Past 所注解的元素必须是某个过去的日期
- @PastOrPresent 所注解的元素必须是过去某个或现在日期
- @Pattern 所注解的元素必须满足给定的正则表达式
- @Size 所注解的元素必须是String、集合或数组,且长度大小需保证在给定范围之内
- @Email 所注解的元素需满足Email格式
@NotNull 验证对象是否不为null, 无法查检长度为0的字符串: 不能为null,但可以为空字符串
@NotBlank 检查约束 (字符串) 是不是Null还有被Trim的长度是否大于0,只对字符串,且会去掉前后空格.: 只能作用在String上,不能为null,而且调用trim()后,长度必须大于0
@NotEmpty 检查(集合)约束元素是否为NULL或者是EMPTY. : 不能为null,并且长度必须大于0
@NotEmpty修饰的String类、Collection、Map、数组,是不能为null或者长度为0的(String Collection Map的isEmpty()方法)
参数校验的两种场景
单个参数校验
在参数前加上校验注解,前提要在类上加入注解@Validated
例如
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| @ResponseBody @PostMapping("/user") public Object login2(@RequestParam("email") @NotBlank(message = "邮箱号不能为空") @Email(message = "邮箱格式不正确") String email, @RequestParam("password") @NotBlank(message = "密码不能为空") @Size(min = 5,max = 20,message = "密码应该在5~20之间") String password){ User user = new User(email,password); return loginService.login(user); }
|
实体类参数校验
在类的属性上添加校验注解
例如
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| package com.example.springboot.dto;
import lombok.Data; import javax.validation.constraints.Email; import javax.validation.constraints.NotNull;
@Data public class LoginUserDTO { @Email(message = "必须是邮箱格式啊!!") @NotNull(message = "邮箱不能为空啊!!") private String email; @NotNull(message = "密码不能为空啊!!") private String password; }
|
实体类中的分组处理
思考:当我有一个方法也要校验LoginUserDTO,但是校验的参数和message不同时,怎么办?
解决一:再去定义一个类,编写参数和注解 。 这样很不优雅
解决二:使用参数分组
注册一个接口
1 2
| public interface Login { }
|
然后对LoginUserDTO中的参数进行分组,Default.class是validation-api自带的默认分组类
1 2 3 4 5 6 7 8 9 10
| @Data public class LoginUserDTO {
@Email(message = "必须是邮箱格式啊!!",groups = Default.class) @NotBlank(message = "邮箱不能为空啊!!",groups = Login.class) private String email;
@NotBlank(message = "密码不能为空啊!!",groups = Default.class) private String password; }
|
然后定义两个方法,在@Validated注解中加入分组即可
1 2 3 4 5 6 7 8 9 10 11 12 13
| @ResponseBody @PostMapping("/login") public Object login(@RequestBody @Validated(Login.class) LoginUserDTO user){ System.out.println(user); return loginService.login(user); }
@ResponseBody @PostMapping("/login2") public Object login2(@RequestBody @Validated(Default.class) LoginUserDTO user){ System.out.println(user); return loginService.login(user); }
|
login方法只校验 email的@NotBlank
login2方法只校验 email的@Email和password的NotBlank
全局异常处理
当校验不成功时产生的异常通过全局异常处理,在有@ControllerAdvice注解的异常处理类中加入以下方法,将其拦截,再将错误信息传输到页面当中
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
| @ExceptionHandler({MethodArgumentNotValidException.class,ConstraintViolationException.class}) @ResponseBody public Object resolveMethodArgumentNotValidException(Exception e){ String defaultMessage = null; if (e instanceof MethodArgumentNotValidException) { MethodArgumentNotValidException me = (MethodArgumentNotValidException) e; defaultMessage = me.getBindingResult().getFieldError().getDefaultMessage(); } if (e instanceof ConstraintViolationException) { ConstraintViolationException ce = (ConstraintViolationException) e; Set<ConstraintViolation<?>> constraintViolations = ce.getConstraintViolations(); if(!CollectionUtils.isEmpty(constraintViolations)){ StringBuilder msgBuilder = new StringBuilder(); for(ConstraintViolation constraintViolation :constraintViolations){ msgBuilder.append(constraintViolation.getMessage()).append(","); } defaultMessage = msgBuilder.toString(); if(defaultMessage.length()>1){ defaultMessage = defaultMessage.substring(0,defaultMessage.length()-1); } return ResultDTO.errorOf(defaultMessage); } } return ResultDTO.errorOf(defaultMessage); }
|