参数校验框架之spring-vaildation
安装使用
在springboot 2.3.x以前的版本中,spring-boot-start-web中会将其默认引入,之后的版本将其剔除,需要手动引入下面的依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
可用的注解
常用来使用的注解包括以下这些:
javax
包里的注解
也就是javax.validation.constraints
包里的注解
-
@Vaild
可以加在方法上、属性上、参数前、构造器上、变量类型前,表示这个地方需要校验 -
@NotNull
加在方法、属性、参数、构造器、变量类型、注解上,校验必须不为null
,支持任何类型 -
@NotBlank
加在方法、属性、参数、构造器、变量类型、注解上,校验必须不为null
或者空值,支持CharSequence
类型 -
@NotEmpty
加在方法、属性、参数、构造器、变量类型、注解上,校验必须不为空值,支持CharSequence
,Collection
,Map
,Array
类型 -
@Null
加在方法、属性、参数、构造器、变量类型、注解上,校验必须为null
,支持CharSequence
,Collection
,Map
,Array
类型 -
@Digits
加在方法、属性、参数、构造器、变量类型、注解上,校验不为空,支持BigDecimal
,BigInteger
,CharSequence
,byte, short, int, long
以及它们的包装类 -
@Email
加在方法、属性、参数、构造器、变量类型、注解上,校验必须不为null
或者空值,支持CharSequence
类型 -
@Future
加在方法、属性、参数、构造器、变量类型、注解上,校验时间必须是未来时间,支持以下类型时间java.util.Date
java.util.Calendar
java.time.Instant
java.time.LocalDate
java.time.LocalDateTime
java.time.LocalTime
java.time.MonthDay
java.time.OffsetDateTime
java.time.OffsetTime
java.time.Year
java.time.YearMonth
java.time.ZonedDateTime
java.time.chrono.HijrahDate
java.time.chrono.JapaneseDate
java.time.chrono.MinguoDate
java.time.chrono.ThaiBuddhistDate
-
@FutureOrPresent
加在方法、属性、参数、构造器、变量类型、注解上,校验时间必须是未来时间或者现在,支持时间类型同@Future
-
@Past
加在方法、属性、参数、构造器、变量类型、注解上,校验时间必须是过去时间,支持时间类型同@Future
-
@PastOrPresent
加在方法、属性、参数、构造器、变量类型、注解上,校验时间必须是过去时间或者现在,支持时间类型同@Future
-
@Max
加在方法、属性、参数、构造器、变量类型、注解上,校验必须为数字且最大为设置的值,支持类型同@Digits
-
@Min
加在方法、属性、参数、构造器、变量类型、注解上,校验必须为数字且最小为设置的值,支持类型同@Digits
-
@Size
加在方法、属性、参数、构造器、变量类型、注解上,校验元素大小必须在设置的最小值与最大值之间,包含边界值,至持CharSequence
,Collection
,Map
,Array
类型 -
@DecimalMax
与@Max
类似,值是一个符合BigDecimal
规则的字符串 -
@DecimalMin
与@Min
类似,值是一个符合BigDecimal
规则的字符串 -
@AssertTrue
加在方法、属性、参数、构造器、变量类型、注解上,校验元素必须true
,支持boolean
和Boolean
类型 -
@AssertFalse
加在方法、属性、参数、构造器、变量类型、注解上,校验元素必须false
,支持boolean
和Boolean
类型 -
@Negative
加在方法、属性、参数、构造器、变量类型、注解上,校验数字必须是一个负数,不包含0
,支持类型同@Digits
-
@NegativeOrZero
加在方法、属性、参数、构造器、变量类型、注解上,校验数字必须是一个负数,包含0
,支持类型同@Digits
-
@Positive
加在方法、属性、参数、构造器、变量类型、注解上,校验数字必须是一个正数,不包含0
,支持类型同@Digits
-
@PositiveOrZero
加在方法、属性、参数、构造器、变量类型、注解上,校验数字必须是一个正数,包含0
,支持类型同@Digits
-
@Pattern
加在方法、属性、参数、构造器、变量类型、注解上,校验字符必须匹配设置的正则表达式,支持CharSequence
类型 -
@List
一个特殊的注解,可以用来与上述注解一起使用,用来为这个元素定义多组规则。
spring里的常用注解
@Validated
同@Valid
,不同的是@Validated
支持分组,而@Valid
不支持分组,相当于是@Valid
的增强版
hibernate里的常用注解
@Length
加在方法、属性、参数、构造器、变量类型、注解上,校验字符串的长度不能超过设置的值,支持String
类型@ISBN
加在方法、属性、参数、构造器、变量类型、注解上,校验字符必须是一个标准的ISBN
图书编号,支持CharSequence
类型@Range
加在方法、属性、参数、构造器、变量类型、注解上,校验数字必须在设置的最小值与最大值之间,包含边界值,支持所有的数字类型@URL
加在方法、属性、参数、构造器、变量类型、注解上,校验字符串必须是一个URL,协议包含http
,https
,file
,ftp
,jar
等java.net.URL
支持的协议@EAN
加在方法、属性、参数、构造器、变量类型、注解上,校验字符必须是一个国际商品编码,支持CharSequence
类型CreditCardNumber
加在方法、属性、参数、构造器、变量类型、注解上,校验字符串必须是一个信用卡号的格式
validation使用方式
参数绑定
@Slf4j
@RestController
public class DemoController {
@RequestMapping("/test")
public String test(@Validated Foo foo, BindingResult bindingResult) {
if(bindingResult.hasErrors()){
for (FieldError fieldError : bindingResult.getFieldErrors()) {
String fieldName = fieldError == null ? "" : fieldError.getField();
String defaultMessage = fieldError == null ? "" : fieldError.getDefaultMessage();
String errorMsg = "[" + fieldName + "]" + defaultMessage;
log.info("捕捉到参数校验异常: {}", errorMsg);
//...
}
return "fail";
}
return "success";
}
}
异常捕获
@Slf4j
@RestController
public class DemoController {
@RequestMapping("/test")
public String test(@Validated Foo foo) {
// ...
return "success";
}
}
@Slf4j
@RestControllerAdvice
public class ExceptionHandle {
/**
* 处理validation框架中的{@link MethodArgumentNotValidException}异常
* <p>该异常一般出在用{@code @RequestBody}注解标记的参数校验,在对象中的参数有问题是抛出</p>
*
* @param e {@link MethodArgumentNotValidException}
* @return string
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
public String doNoValidExceptionHandler(MethodArgumentNotValidException e) {
// 默认捕获第一个不符合校验规则的错误信息
return validationExceptionHandler(e.getBindingResult(), e.getMessage());
}
/**
* 处理validation框架中的{@link ConstraintDeclarationException}异常
* <p>该异常一般出在用{@code @RequestParam}注解标记的参数校验</p>
*
* @param e {@link ConstraintDeclarationException}
* @return string
*/
@ExceptionHandler(ConstraintDeclarationException.class)
public String doNoValidExceptionHandler(ConstraintDeclarationException e) {
String errorMsg = e.getMessage();
log.info("捕捉到参数校验异常: [{}]", errorMsg, e);
return errorMsg;
}
/**
* 处理validation框架中的{@link BindException}异常
* <p>该异常一般出在用{@code @RequestBody}注解标记的参数校验,在对象嵌套类型参数有问题时抛出</p>
*
* @param e {@link BindException}
* @return string
*/
@ExceptionHandler(BindException.class)
public String doNoValidExceptionHandler(BindException e) {
return validationExceptionHandler(e.getBindingResult(), e.getMessage());
}
/**
* 处理validation框架的异常
*
* @param bindingResult {@link BindingResult} 异常绑定的对象信息
* @param message 异常信息
* @return string
*/
private String validationExceptionHandler(BindingResult bindingResult, String message) {
// 一般只捕获第一个不符合校验规则的错误信息即可,与配置的快速失败搭配更好
// 错误字段对象
FieldError fieldError = bindingResult.getFieldError();
// 错误字段名
String fieldName = fieldError == null ? "" : fieldError.getField();
// 具体错误信息
String defaultMessage = fieldError == null ? "" : fieldError.getDefaultMessage();
String errorMsg = "[" + fieldName + "]" + defaultMessage;
log.info("捕捉到参数校验异常: [{}]", message);
return errorMsg;
}
}
ConstraintDeclarationException
类型异常出现的方式
@Slf4j
@Validated
@RestController
public class DemoController {
@RequestMapping("/test")
public String test(@RequestParam("foo") @NotBlank(message = "foo不可为空") String foo) {
// ...
return "success";
}
}
这种校验方式会抛出ConstraintDeclarationException
异常,并且如果使用校验结果绑定的方式,并不会获取到错误的信息,只能通过异常捕获的方式处理。而且还需要在类上加@Validated
,如果不加这种简单参数校验将不会有任何作用。
配置快速失败
@Configuration
public class ValidatorConfiguration {
@Bean
public Validator validator() {
ValidatorFactory validatorFactory = Validation.byProvider(HibernateValidator.class)
.configure()
// 快速失败
.failFast(true)
.buildValidatorFactory();
return validatorFactory.getValidator();
}
}
分组校验
分组校验需要定义一个空接口,然后将这个接口设置到校验的graoups
里和@Validate
里即可。
public class Foo {
@NotNull(message="姓名不能为null", groups={Test.class})
@NotBlank(message="姓名不能为空字符串", groups={TestBlank.class})
private String name;
public interface Test {}
public interface TestBlank {}
}
@Slf4j
@RestController
public class DemoController {
@RequestMapping("/test")
public String test(@Validated(Foo.Test.class) Foo foo) {
// ...
return "success";
}
@RequestMapping("/test/blank")
public String test(@Validated(Foo.TestBlank.class) Foo foo) {
// ...
return "success";
}
}
嵌套校验
嵌套校验就是需要对一个对象里的对象属性进行嵌套校验,具体如下
public class Foo {
@NotNull(message="姓名不能为null", groups={Test.class})
@NotBlank(message="姓名不能为空字符串", groups={TestBlank.class})
private String name;
@Valid// 在需要嵌套校验的字段上加入@Valid或者@Vaildated
@NotNull(message="inner不可为null")
private InnerFoo inner;
public interface Test {}
public interface TestBlank {}
}
public class InnerFoo {
@NotBlank(message="手机号不能为空")
private String mobile;
}