参数校验
基本用法
Spring Boot Validation 提供了一系列注解,用于在实体类中定义验证规则。以下是一些常用的校验相关的注解及其功能以及用法:
1.@NotNull
: 校验元素值不能为 null。如果元素为null,则验证失败。通常用于字段级别的验证。
2.@NotBlank
: 校验字符串元素值不能为 null 或空字符串。必须包含至少一个非空格字符(即执行trim()之后不为’’)。如果元素为null或者‘‘,则验证失败。通常用于String
类型的字段校验。
3.NotEmpty
: 校验集合元素或数组元素或者字符串是否非空。通常作用于集合字段或数组字段,此时需要集合或者数字的元素个数大于0。也可以作用于字符串,此时校验字符串不能为null或空串(可以是一个空格)。注意与@NotBlank
的使用区别。
4.@Length
: 校验字符串元素的长度。作用于字符串。
5.@Size
: 校验集合元素个数或字符串的长度在指定范围内。在集合或字符串字段上添加 @Size
注解。
@Size(min = 1, max = 10, message = "Number of items must be between 1 and 10") private List<String> items;
@Size(min = 5, max = 20, message = "Length must be between 5 and 20 characters") private String username;
|
6.@Min
: 校验数字元素的最小值。
7.@Max
: 校验数字元素的最大值。
9.@DecimalMax
: 作用于BigDecimal
类型字段, 校验字段的最大值,支持比较的值为字符串表示的十进制数。通常搭配它的inclusive()
使用,区别边界问题。value
属性表示最大值,inclusive 属性表示是否包含最大值。
@DecimalMax(value = "100.00", inclusive = true, message = "Value must be less than or equal to 100.00") private BigDecimal amount;
|
10.@DecimalMin
: 作用于BigDecimal
类型字段, 校验字段的最小值,支持比较的值为字符串表示的十进制数。通常搭配它的inclusive()
使用,区别边界问题。value
属性表示最小值,inclusive 属性表示是否包含最小值。
11.@Email
: 校验字符串元素是否为有效的电子邮件地址。可以通过regexp
自定义邮箱匹配正则。
12.@Pattern
: 根据正则表达式校验字符串元素的格式。
@Pattern(regexp = "[a-zA-Z0-9]+", message = "Only alphanumeric characters are allowed") private String username;
|
13.@Digits
: 校验数字元素的整数部分和小数部分的位数。作用于BigDecimal
,BigInteger
,字符串,以及byte
, short
,int
, long
以及它们的包装类型。
@Digits(integer = 5, fraction = 2, message = "Number must have up to 5 integer digits and 2 fraction digits") private BigDecimal amount;
|
14.@Past
: 校验日期或时间元素是否在当前时间之前。即是否是过去时间。作用于Date相关类型的字段。
15.@Future
: 校验日期或时间元素是否在当前时间之后。即是否是未来时间。作用于Date相关类型的字段。
16.**@AssertTrue
**:必须是true
17.**@AssertFalse
**:必须是false
—— 2024/05/18 更新:
@Valid
:注解校验提交的List
、Object
等,进行方法级验证,以及用于标记成员属性以进行验证。不过,该注解不支持分组验证。
@Data public class UserAccount {
@NotNull @Size(min = 4, max = 15) private String password;
@NotBlank private String name; }
|
@RequestMapping(value = "/saveBasicInfo", method = RequestMethod.POST) public String saveBasicInfo( @Valid @ModelAttribute("useraccount") UserAccount useraccount, BindingResult result, ModelMap model) { if (result.hasErrors()) { return "error"; } return "success"; }
|
当然也可以在pojo层用
public class UserAddress {
@NotBlank private String countryCode;
}
|
public class UserAccount { @Valid @NotNull(groups = AdvanceInfo.class) private UserAddress useraddress; }
|
@Validated
:对于分组级(Group-Level)验证,必须使用 Spring 的 @Validated
,它是 JSR-303 的 @Valid
的变体,用于方法级。
有时我们的入参是基本类型,例如使用@RequestParam
或@PathVariable
标记的参数
@Validated @RestController @RequestMapping("/validation") public class ValidateController { ... @GetMapping("/boy-friends") public ResponseEntity<BoyFriend> updateBoyFriend(@NotBlank @RequestParam("name") String name) { ... } }
|
我们只需要给参数添加上相应的约束注解,例如@NotBlank
,然后再给Controller类添加上@Validated
即可。
分组验证
有时一个类被多个方法使用,而每个方法对入参的要求却不一样,这种情况怎么办呢?例如我们的BoyFriend
,里面有一个体重的属性,创建时要求不能高于85kg,由于条件苛刻,于是修改的时候要求不能高于100kg。
Spring提供了一种解决方法,那就是使用分组。每个注解里面都可以设置其属于哪些分组,在验证的时候只验证属于自己分组的那些约束。
例如我们这里设置两个分组:创建和更新,当调用创建方法的时候就只验证属于创建分组的约束,不高于85kg…
public class BoyFriend { @Max(groups = BoyFriendCreate.class, value = 85) @Max(groups = BoyFriendUpdate.class, value = 100) private Integer weight; }
|
分组必须是接口,例如我们这里定义了两个分组
public interface BoyFriendCreate { }
public interface BoyFriendUpdate { }
|
先给Controller类添加@Validated
,然后给方法添加带有分组信息的@Validated
@Validated(BoyFriendUpdate.class) @PatchMapping("/boy-friends") public ResponseEntity<BoyFriend> updateBoyFriend(@Valid @RequestBody BoyFriend boy) { return ResponseEntity.ok(boy); }
|
全局异常处理
每个Controller
方法中如果都写一遍BindingResult
信息的处理还是很繁的。当我们写了@validated
注解,不写BindingResult
的时候,Spring 就会抛出异常。因此,我们可以通过全局异常处理的方式统一处理校验异常,从而免去重复编写异常信息的代码。全局异常处理类只需要在类上标注@RestControllerAdvice
,并在处理相应异常的方法上使用@ExceptionHandler
注解,写明处理哪个异常即可。
全局异常处理类 GlobalExceptionHandler
@RestControllerAdvice public class GlobalExceptionHandler { private static final String VALID_FAIL_MSG = "参数检验不通过";
@ExceptionHandler(BindException.class) public Result<List<String>> bindExceptionHandler(BindException e) { List<FieldError> fieldErrors = e.getBindingResult().getFieldErrors(); List<String> collect = fieldErrors.stream().map(o -> o.getDefaultMessage()).toList(); return Result.error(VALID_FAIL_MSG,collect); }
@ExceptionHandler(MethodArgumentNotValidException.class) public Result<List<String>> methodArgumentNotValidExceptionHandler(MethodArgumentNotValidException e) { List<FieldError> fieldErrors = e.getBindingResult().getFieldErrors(); List<String> collect = fieldErrors.stream().map(o -> o.getDefaultMessage()).collect(Collectors.toList()); return Result.error(VALID_FAIL_MSG,collect); }
@ExceptionHandler(ConstraintViolationException.class) public Result<List<String>> constraintViolationExceptionHandler(ConstraintViolationException e) { Set<ConstraintViolation<?>> constraintViolations = e.getConstraintViolations(); List<String> collect = constraintViolations.stream().map(o -> o.getMessage()).collect(Collectors.toList()); return Result.error(VALID_FAIL_MSG,collect); }
@ExceptionHandler(Exception.class) public Result handleException(Exception e){ e.printStackTrace(); return Result.error(StringUtils.hasLength(e.getMessage())? e.getMessage() : "操作失败"); } }
|
参考链接
@Valid 注解校验提交的List(list 集合) javax.validation.Valid_validlist-CSDN博客
秒懂SpringBoot之参数验证全解析(@Validated与@Valid) - 知乎 (zhihu.com)
Spring 中 @Valid 和 @Validated 注解的区别 - spring 中文网 (springdoc.cn)