JSR
:Java Specification Requests的缩写,意思是Java规范提案
Bean Validation
就是这个JSR规范之一 数据验证框架
的标准 JSR303是专家组成员向JCP提交的第1版Bean Validation,即针对bean数据校验提出的一个规范,使用注解方式实现数据校验
。后面有升级版本JSR349及JSR380。
JSR # Bean Validation官网地址:https://jcp.org/en/jsr/summary?id=bean+validation
JSR303 Bean Valiadation第一版
伴随着JAVAEE 6在2009年发布,Hibernate实现版本4.3.1.FinalJSR349 Bean Valiadation 1.1
伴随着JAVAEE 7在2013年发布,Hibernate实现版本5.1.1.FinalJSR380 Bean Valiadation 2.0
伴随着JAVAEE 8在2017年发布,完全兼容低版本的JAVA SE,Hibernate实现版本6.0.1.FinalBean Valiadation 与 Hibernate Validation
JSR(Bean Valiadation)
规定一些校验规范即校验注解,如@Null,@NotNull,@Pattern,位于javax.validation.constraints包下,只提供规范不提供实现
Hibernate Validation
是对Bean Valiadation这个规范的实践,提供相应的实现
,并增加一些其他校验注解,如@Length,@Range等等,位于org.hibernate.validator.constraints包下2.0.1.Final
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>2.0.1.Final</version>
</dependency>
Hibernate-Validator
是Hibernate项目中的一个数据校验框架,是Bean Validation一种实现
附加的constraint(约束)
6.0.16.Final
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.0.16.Final</version>
</dependency>
Java EE
改名为Jakarta EE
,故javax.validation
相关的api在jakarta.validation
的包下2.0.2
<dependency>
<groupId>jakarta.validation</groupId>
<artifactId>jakarta.validation-api</artifactId>
<version>2.0.2</version>
</dependency>
spring-boot-starter-validation:2.3.12.RELEASE
jakarta
,导包还是javax
,应该是个过渡吧<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
@Validated
注解springmvc
提供注解,非javax.validation
的注解import org.springframework.validation.annotation.Validated;
import javax.validation.constraints.Max;
import javax.validation.constraints.NotEmpty;
@RestController
@Validated
public class UserController1 {
@GetMapping("/getUser")
public String getUser(@NotEmpty(message = "name不能为空") @RequestParam("name") String name,
@Max(value = 150) @RequestParam("age") Integer age) {
return "success";
}
}
执行请求:http://localhost:8080/getUser?name=&age=200
message
属性(抛错提示),也可以不设置(默认中文错误提示
)@Valid
或@Validated
注解 设置分组
,后面会讲javax.validation
提供注解import javax.validation.Valid;
@RestController
public class UserController2 {
@PostMapping("/saveUser")
public String saveUser(@Valid @RequestBody User user) {
System.out.println(user);
return "success";
}
}
import org.hibernate.validator.constraints.Range;
import org.hibernate.validator.constraints.Length;
import javax.validation.Valid;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;
@Data
public class User {
// 可以为 null,若不为 null,则长度为 [5, 17]
@Length(min = 5, message = "userName长度须在[5,17]")
private String userName;
// @Size不能验证Integer,适用于String、List、Map、数组
@Size(min = 1, max = 3, message = "password长度须在[1,3]")
private String password;
// list 可以为 null,若不为 null,则长度为 [1, 5]
@Size(min = 1, max = 5, message = "list的Size在[1,5]")
private List<String> list;
@NotNull
@Valid // 级联校验,该注解将会校验自定义类
private DetailInfo detailInfo;
}
@Data
class DetailInfo {
@NotEmpty(message = "name不能为空")
private String name;
// 匹配任何数字
@Pattern(regexp = "^\\d+$", message = "itemCode需要数字")
private String itemCode;
@Range(min = 10, max = 100, message = "range在[10,100]之间")
private int age;
}
注解 | 说明 | 支持类型 |
---|---|---|
@Null | 必须为null | |
@NotNull | 必须不为null | |
@NotEmpty | 必须不为null且长度大于0 | String、集合、Map、数组 |
@NotBlank | 必须不为null且去除首尾空格后长度大于0 | String |
@AssertTrue | 必须为true | boolean和Boolean |
@AssertFalse | 必须为false | boolean和Boolean |
@Min(value) | 必须是一个数字其值必须大于或等于指定的最小值值为null不校验 | String、Number |
@Max(value) | 必须是一个数字其值必须小于或等于指定的最大值值为null不校验 | String、Number |
@DecimalMin(value) | 必须是一个数字其值必须大于或等于指定的最小值(支持小数)值为null不校验 | String、Number |
@DecimalMax(value) | 必须是一个数字其值必须小于或等于指定的最大值(支持小数)值为null不校验 | String、Number |
@Size(max, min) | 元素的大小必须在指定的范围内值为null不校验 | String、集合、Map、数组 |
@Pattern(regexp = ) | 正则表达式校验值为null不校验 | |
@Digits(integer,fraction) | 验证整数位数和小数位数上限值为null不校验 | String、Number |
@Past | 必须是一个过去的日期值为null不校验 | 日期类型 |
@Future | 必须是一个将来的日期值为null不校验 | 日期类型 |
必须是电子邮箱地址值为null不校验 | ||
@Length(min=, max=) | 字符串长度必须在指定的范围内值为null不校验 | String |
@Range(min=, max=) | 数字或字符串数值必须在指定范围内值为null不校验 | String、Number |
@URI | 必须是一个有效的URL字符串值为null不校验 | String |
注意:值为null不校验的注解一般和@NotNull一起使用
分组
设置不同的校验注解javax.validation.groups.Default
,添加Default,为了没有分组的注解生效@RestController
public class UserController3 {
@PostMapping("/saveUser")
public String saveUser(@Validated(value = {User.SaveGroup.class, Default.class}) @RequestBody User user) {
System.out.println(user);
return "success";
}
@PostMapping("/updateUser")
public String updateUser(@Validated(value = {User.UpdateGroup.class, Default.class}) @RequestBody User user) {
System.out.println(user);
return "success";
}
}
@Data
public class User {
// 默认就是Default分组
public interface SaveGroup {
}
public interface UpdateGroup {
}
// 新增时必须为空
@Null(groups = SaveGroup.class)
// 修改是必须不为空
@NotNull(groups = UpdateGroup.class)
private Long id;
@NotEmpty
private String userName;
}
xxxValidator
中,如下@Documented
@Constraint(validatedBy = {StatusValidator.class})
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
@Retention(RUNTIME)
public @interface Status {
String message() default "状态标识只能为0或1";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
自定义注解
,第二个为支持类型
public class StatusValidator implements ConstraintValidator<Status, String> {
@Override
public boolean isValid(String bool, ConstraintValidatorContext constraintValidatorContext) {
// 模仿一般的注解,值为null不做校验,和@NotEmpty一起使用
if (bool == null) {
return true;
}
// 包含在0和1里面返回true通过,不包含则不通过
return Arrays.asList("0","1").contains(bool);
}
}
默认
普通模式 快速失败
返回模式 开启快速失败返回模式
@Configuration
public class HibernateValidatorConfiguration {
@Bean
public Validator validator(){
ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class )
.configure()
// true 快速失败返回模式 false 普通模式
.addProperty( "hibernate.validator.fail_fast", "true" )
.buildValidatorFactory();
Validator validator = validatorFactory.getValidator();
return validator;
}
}
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
@ResponseBody
public ResultEntity<String> handleBindException(Exception ex) {
// post请求参数校验
if (ex instanceof MethodArgumentNotValidException) {
MethodArgumentNotValidException exs = (MethodArgumentNotValidException) ex;
FieldError fieldError = exs.getBindingResult().getFieldError();
return ResultEntity.fail(400, fieldError.getDefaultMessage(), null);
}
// get请求参数校验
if (ex instanceof ConstraintViolationException) {
ConstraintViolationException exs = (ConstraintViolationException) ex;
Set<ConstraintViolation<?>> violations = exs.getConstraintViolations();
for (ConstraintViolation<?> item : violations) {
return ResultEntity.fail(400, item.getMessage(), null);
}
}
return ResultEntity.fail(500, ex.getMessage(), null);
}
}
@Data
@AllArgsConstructor
@NoArgsConstructor
class ResultEntity<T> {
private Integer code;
private String message;
private T data;
public static <T> ResultEntity<T> fail(Integer code, String msg, T t) {
return new ResultEntity<T>(code, msg, t);
}
}