一、添加依赖
基于SpringBoot,只需要给项目添加spring-boot-starter-web 依赖就够了,但在SpringBoot 2.3.1之后,spring-boot-starter-validation已经不包括在spring-boot-starter-web中,需要手动加上!
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
二、验证
1.校验参数
/**
* @author Ian Zhao
* @description
* @date 2021/6/18 10:40 上午
* ^string : 匹配以 string 开头的字符串
* string$ :匹配以 string 结尾的字符串
* ^string$ :精确匹配 string 字符串
* (^Man$|^Woman$|^UGM$) : 值只能在 Man,Woman,UGM 这三个值中选择
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class PersonRequest {
@NotNull(message = "classId 不能为空")
@Email
private String classId;
@Size(max = 10)
@NotNull(message = "name 不能为空")
private String name;
@Pattern(regexp = "(^Man$|^Woman$|^UGM$)", message = "sex 值不在可选范围内")
@NotNull(message = "sex 不能为空")
private String sex;
}
2.自定义捕获异常
GlobalExceptionHandler
自定义异常处理器可以帮助我们捕获异常,并进行一些简单的处理。
/**
* @author Ian Zhao
* @description 自定义异常处理器
* @date 2021/6/17 4:49 下午
*/
@ControllerAdvice(assignableTypes = {PersonController.class})
public class GlobalExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<Map<String, String>> handleValidationException(MethodArgumentNotValidException ex) {
Map<String, String> errors = new HashMap<>();
ex.getBindingResult().getAllErrors().forEach(
error -> {
String fieldName = ((FieldError) error).getField();
String errorMessage = error.getDefaultMessage();
errors.put(fieldName, errorMessage);
}
);
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(errors);
}
@ExceptionHandler(ConstraintViolationException.class)
public ResponseEntity<String> handleConstraintViolationException(ConstraintViolationException e) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.getMessage());
}
}
3.验证请求体
验证请求体即被@RequestBody注解标记的方法参数。
PersonController
我们在需要验证的参数上加上了@Valid注解,如果验证失败,它将抛出MethodArgumentNotValidException。默认情况下,Spring 会将此异常转换为 HTTP Status 400(错误请求)。
/**
* @author Ian Zhao
* @description TODO
* @date 2021/6/18 10:39 上午
*/
@RestController
@RequestMapping("/api/person")
@Validated
public class PersonController {
@PostMapping
public ResponseEntity<PersonRequest> save(@RequestBody @Valid PersonRequest personRequest) {
return ResponseEntity.ok().body(personRequest);
}
}
4.postman测试

三、自定义Validator
如果自带的校验注解无法满足你的需求的话,你还可以自定义实现注解。
案例一:校验特定字段的值是否在可选范围
比如我们现在多了这样一个需求:PersonRequest 类多了一个 Region 字段,Region 字段只能是China、China-Taiwan、China-HongKong这三个中的一个。
第一步,你需要创建一个注解 Region。
/**
* 自定义注解
* @author zhaodashuai7
*/
@Target({FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = RegionValidator.class)
@Documented
public @interface Region {
String message() default "Region 值不在可选范围内";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
第二步,你需要实现 ConstraintValidator接口,并重写isValid 方法。
/**
* @Author: Ian Zhao
* @Date: 2021/6/18 11:10 上午
*/
public class RegionValidator implements ConstraintValidator<Region,String> {
@Override
public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) {
HashSet<Object> regions = new HashSet<>();
regions.add("China");
regions.add("China-Taiwan");
regions.add("China-HongKong");
return regions.contains(value);
}
}
现在你就可以使用这个注解:
@Region
private String region;
案例二:校验电话号码
校验我们的电话号码是否合法,这个可以通过正则表达式来做,相关的正则表达式都可以在网上搜到,你甚至可以搜索到针对特定运营商电话号码段的正则表达式。
PhoneNumber.java
/**
* @Author: Ian Zhao
* @Date: 2021/6/18 11:31 上午
* @Description TODO
*/
@Documented
@Constraint(validatedBy = PhoneNumberValidator.class)
@Target({FIELD,PARAMETER})
@Retention(RUNTIME)
public @interface PhoneNumber {
String message() default "Invalid phone number";
Class[] groups() default {};
Class[] payload() default {};
}
PhoneNumberValidator.java
/**
* @Description
* @Author: Ian Zhao
* @Date: 2021/6/18 11:35 上午
*/
public class PhoneNumberValidator implements ConstraintValidator<PhoneNumber,String> {
@Override
public boolean isValid(String phoneField, ConstraintValidatorContext constraintValidatorContext) {
if (StrUtil.isBlankIfStr(phoneField)) {
return true;
}
String regExp = "^(13[0-9]|14[01456879]|15[0-35-9]|16[2567]|17[0-8]|18[0-9]|19[0-35-9])\\d{8}$";
return phoneField.matches(regExp);
}
}
使用注解:
@PhoneNumber(message = "phoneNumber 格式不正确")
@NotNull(message = "phoneNumber 不能为空")
private String phoneNumber;
测试
四、总结
1、注解
常用校验注解总结
JSR303 定义了 Bean Validation(校验)的标准 validation-api,并没有提供实现。Hibernate Validation是对这个规范/规范的实现 hibernate-validator,并且增加了 @Email、@Length、@Range 等注解。Spring Validation 底层依赖的就是Hibernate Validation。
JSR 提供的校验注解:
@Null 被注释的元素必须为 null
@NotNull 被注释的元素必须不为 null
@AssertTrue 被注释的元素必须为 true
@AssertFalse 被注释的元素必须为 false
@Min(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值
@Max(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
@DecimalMin(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值
@DecimalMax(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
@Size(max=, min=) 被注释的元素的大小必须在指定的范围内
@Digits (integer, fraction) 被注释的元素必须是一个数字,其值必须在可接受的范围内
@Past 被注释的元素必须是一个过去的日期
@Future 被注释的元素必须是一个将来的日期
@Pattern(regex=,flag=) 被注释的元素必须符合指定的正则表达式
Hibernate Validator 提供的校验注解:
@NotBlank(message =) 验证字符串非 null,且长度必须大于 0
@Email 被注释的元素必须是电子邮箱地址
@Length(min=,max=) 被注释的字符串的大小必须在指定的范围内
@NotEmpty 被注释的字符串的必须非空
@Range(min=,max=,message=) 被注释的元素必须在合适的范围内
2、注意
@Validated 放在方法上
@Valid 放在参数上
3、拓展
经常有小伙伴问到:“@NotNull 和 @Column(nullable = false) 两者有什么区别?”
我这里简单回答一下:
@NotNull是 JSR 303 Bean 验证批注,它与数据库约束本身无关。
@Column(nullable = false) : 是 JPA 声明列为非空的方法。
总结来说就是即前者用于验证,而后者则用于指示数据库创建表的时候对表的约束。