title: Spring Validation 使用总结
date: 2022-10-12 17:35:57
tags:
数据的校验是交互式网站一个不可或缺的功能,前端的 JS 校验可以涵盖大部分的校验职责,如用户名唯一性,生日格式,邮箱格式校验等等常用的校验。但是为了避免用户绕过浏览器,使用 HTTP 工具直接向后端请求一些违法数据,服务端的数据校验也是必要的,可以防止脏数据落到数据库中
常用校验
1、SR303/JSR-349
JSR303 是一项标准,只提供规范不提供实现,规定一些校验规范即校验注解,如 @Null,@Pattern,位于 javax.validation.constraints 包下。JSR-349 是其的升级版本,添加了一些新特性
2、Hibernate Validation
Hibernate Validation 是对这个规范的实现,并增加了一些其他校验注解,如 @Email,@Length,@Range 等等
3、Spring Validation
Spring Validation 对Hibernate Validation 进行了二次封装,在 SpringMVC 模块中添加了自动校验,并将校验信息封装进了特定的类中
注解 | 含义 | |
---|---|---|
空值判断 | @Null | 验证对象是否为NULL |
@NotNull | 验证对象是否不为NULL,但可以为 EMPTY("", " ", " "),无法查检长度为 0 的字符串 | |
@NotBlank | 验证String 对象是否不为 NULL,还有被 Trim 的长度是否大于 0,只能用在 String 字符串类型上,且会去掉前后空格 | |
@NotEmpty | 验证对象是否不为NULL 或者是 EMPTY(""),长度必须大于 0 (" ", " ") | |
布尔检查 | @AssertTrue | 验证 Boolean 对象是否为 True |
@AssertFalse | 验证 Boolean 对象是否为 False | |
长度检查 | @Size(min, max) | 验证对象(Array, Collection , Map, String)长度是否在给定的范围之内 |
@Length(min, max) | 验证字符串长度是否介于 min 和 max 之间 | |
日期检查 | @Past | 验证 Date 和 Calendar 对象是否在当前时间之前,即过去的日期 |
@Future | 验证 Date 和 Calendar 对象是否在当前时间之前,即过去的日期 | |
正则检查 | @Pattern(regexp, flags) | 验证 String 对象是否符合正则表达式的规则
|
数值检查 (建议使用在 String,Integer 类型 不建议使用在 int 类型上 因为表单值为 "" 时无法转换为 int 但可以转换为 String 为 "" Integer 为 NULL) |
@Min(val) | 验证 Number 和 String 对象是否大等于指定的值 |
@Max(val) | 验证 Number 和 String 对象是否小等于指定的值 | |
@DecimalMax(val) | 被标注的值必须不大于约束中指定的最大值,这个约束的参数是一个通过 BigDecimal 定义的最大值的字符串表示 .小数 存在精度 | |
@DecimalMin(val) | 被标注的值必须不小于约束中指定的最小值,这个约束的参数是一个通过 BigDecimal 定义的最小值的字符串表示 .小数 存在精度 | |
@Digits | 验证 Number 和 String 的构成是否合法 | |
@Digits(integer, fraction) | 验证字符串是否是符合指定格式的数字,integer 指定整数精度,fraction 指定小数精度 | |
@Range(min, max) | 被指定的元素必须在合适的范围内 | |
验证是否是邮件地址,如果为 NULL,不进行验证,算通过验证 | ||
@CreditCardNumber | 信用卡验证 | |
@ScriptAssert(lang, script, alias) | ||
@URL(protocol, host, port, regexp, flags) | ||
@Valid | 递归的对关联对象进行校验, 如果关联对象是个集合或者数组,那么对其中的元素进行递归校验,如果是一个 Map,则对其中的值部分进行校验.(是否进行递归验证) |
@Valid、@Validated 注解都可以实现数据的验证,表示开启数据验证
1、包位置不同
javax.validation.Valid
org.springframework.validation.annotation.Validated
,是 @Valid 的一次封装,Spring 提供的校验机制使用2、Spring Validation 验证框架提供了 @Validated 注解对参数进行验证,符合 Spring’s JSR-303 规范;而 @Valid 注解是 javax 提供的,符合标准的 JSR-303 规范
3、当使用仅是注解字段属性并验证规范,@Validated 和 @Valid 注解的功能是相同的
4、@Validated 注解可以用于方法、构造函数、方法参数上;而 @Valid 还可以用于成员属性(字段)之上
5、@Validated 注解可以使用分组校验的功能,为同一个对象属性提供不同分组,并根据分组来校验属性参数;而 @Valid 注解不支持分组验证
6、@Valid 注解支持嵌套验证,当类的属性是一个复杂对象时,可以使用 @Valid 对该属性对象中的属性同时进行校验;@Validated 并不支持在属性上使用
导入依赖
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-validationartifactId>
<version>${spring.boot.version}version>
dependency>
服务接口接收单个简单参数时,可以在方法参数中直接使用校验注解。单参数校验时,还需要在 Controller 层控制器类中使用 @Validated 标注才会生效
@RestController
@Validated
public class ValidateController {
@PostMapping("/simple")
public Result simple(@RequestParam("validate") @NotBlank(message = "用户编码不能为空") String validate) {
System.out.println(validate + "参数值");
return Result.success("成功");
}
}
传入多个单参数为空时:
@RestController
@Validated
public class ValidateController {
@PostMapping("/simple")
public Result simple(@RequestParam("validate") @NotBlank(message = "用户编码不能为空") String validate,
@RequestParam("id") @NotBlank(message = "ID 不能为空") String id) {
System.out.println(validate + "参数值" + id);
return Result.success("成功");
}
}
在实体类中使用校验注解标注需要校验的字段后,还需要在请求层接收参数时开启参数校验,只需要在 Controller 接口层的参数中使用 @Validated 标注,在接口接收到请求参数时会自动进行校验
校验时可以使用 BindingResult 返回错误信息(注:添加了 BindingResult 就不会报错了),需配合 @RequestBody
或 RequestPart
一起使用,同时请求参数要在请求体里(重点是这个),否则会报 IllegalStateException 异常
@RestController
public class ValidateController {
@PostMapping("/getUser")
public Result getUser(@Validated @RequestBody User user, BindingResult result) {
if (result.hasErrors()) {
return Result.fail("校验失败" + result.getFieldError().getDefaultMessage());
}
return Result.success("success");
}
}
实体类得加上 @Data 注解,即 getter/setter 方法,不然校验不到
@Data
public class User {
@NotNull(message = "id 不能为空")
private Integer id;
@NotBlank(message = "姓名不能为空")
private String name;
private String gender;
@Pattern(regexp = "^((?:19[2-9]\\d{1})|(?:20(?:(?:0[0-9])|(?:1[0-8]))))\\-((?:0?[1-9])|(?:1[0-2]))\\-((?:0?[1-9])|(?:[1-2][0-9])|30|31)$")
private String birthday;
@DecimalMin("0")
private BigDecimal balance;
@Valid
private List<Pet> pets;
}
@Data
class Pet {
@Min(0)
private double weight;
private String name;
}
使用 Validation 校验异常后,当参数发生异常时,会抛出 MethodArgumentNotValidException 类型的异常,为了程序报错更通俗易懂以及方便,无需在每个接口都手动返回异常信息,可以定义全局异常来捕获该类型的异常,并统一返回结果信息
@RestControllerAdvice
//@ControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
// @ResponseBody
@ExceptionHandler(value = MethodArgumentNotValidException.class)
public Result notValidException(MethodArgumentNotValidException exception) {
String message = exception.getBindingResult().getFieldError().getDefaultMessage();
log.error("参数校验异常: {}", message);
return Result.fail(message);
}
}
当参数传递异常时,可能还会出现 BindingException,使用简单参数校验时,会出现 ConstraintViolationException,可根据实际情况定义全局异常