1. 引入依赖
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.0.17.Final</version>
</dependency>
2. 配置全局捕获异常处理
@ControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
@ExceptionHandler(value = BindException.class)
@ResponseBody
public Object handleBindException(BindException ex) {
log.info("BindException",ex);
StringBuilder errorMsg = new StringBuilder();
List<FieldError> allErrors = ex.getFieldErrors();
allErrors.forEach(fieldError -> errorMsg.append(fieldError.getField())
.append(":").append(fieldError.getDefaultMessage()).append("; "));
return ResultModel.error(ResultStatus.FAIL_ERR, errorMsg.toString());
}
@ExceptionHandler(value = MethodArgumentNotValidException.class)
@ResponseBody
public Object handleMethodArgumentNotValidException(MethodArgumentNotValidException ex) {
log.info("MethodArgumentNotValidException",ex);
StringBuilder errorMsg = new StringBuilder();
List<FieldError> allErrors = ex.getFieldErrors();
allErrors.forEach(fieldError -> errorMsg.append(fieldError.getField())
.append(":").append(fieldError.getDefaultMessage()).append("; "));
return ResultModel.error(ResultStatus.FAIL_ERR, errorMsg.toString());
}
}
3. 常用的验证注解
@Data
public class AddUserDTO {
@NotBlank(message = "姓名不能为空")
@Size(max = 20, message = "姓名的长度不能超过20")
@ApiModelProperty(value = "姓名",required = true)
private String name;
@NotNull
@Pattern(regexp = "^(13[0-9]|14[01456879]|15[0-35-9]|16[2567]|17[0-8]|18[0-9]|19[0-35-9])\\d{8}$", message = "请输入正确的手机号")
@ApiModelProperty(value = "手机号",required = true)
private String phone;
@Max(value = 10, message = "年龄不能超过10岁")
@Min(value = 1, message = "年龄不能低于1岁")
@ApiModelProperty("年龄")
private Integer age;
@ApiModelProperty("性别")
private Integer sex;
@ApiModelProperty("地址")
private String address;
@NotEmpty(message = "请选择所要管理的加油站")
@ApiModelProperty(value = "加油站id集合",required = true)
private List<Integer> gasStationIds;
@NotNull(message = "请选择用户角色")
@ApiModelProperty(value = "用户角色类型,0-站长,1-员工,2-运维", required = true)
private Integer role;
}
4. 注意事项
public Object create(@RequestBody @Valid AddUserDTO dto){}
public Object create(@NotNull String str){}
@AssertFalse @AssertTrue 检验boolean类型的值
@DecimalMax @DecimalMin 限定被标注的属性的值的大小
@Digits(intege=,fraction=) 限定被标注的属性的整数位数和小数位数
@Future检验给定的日期是否比现在晚
@Past 校验给定的日期是否比现在早
@Max检查被标注的属性的值是否小于等于给定的值
@Min检查被标注的属性的值是否大于等于给定的值
@NotNull检验被标注的值不为空
@Null 检验被标注的值为空
@Pattern(regex=,flag=) 检查该字符串是否能够在match指定的情况下被regex定义的正则表达式匹配
@Size(min=,max=) 检查被标注元素的长度
@Valid递归的对关联的对象进行校验
5.validator 注解
1. 空与非空检查
|注解 |支持Java类型 |说明
|@Null |Object |为null
|@NotNull |Object |不为null
|@NotBlank |CharSequence |不为null,且必须有一个非空格字符
|@NotEmpty |CharSequence、Collection、Map、Array |不为null,且不为空(length/size>0)
2. Boolean值检查
|注解 |支持Java类型 |说明 |备注
|@AssertTrue |boolean、Boolean |为true |为null有效
|@AssertFalse |boolean、Boolean |为false |为null有效
3. Boolean值检查
|注解 |支持Java类型 |说明 |备注
|@AssertTrue |boolean、Boolean |为true |为null有效
|@AssertFalse |boolean、Boolean |为false |为null有效
4. 日期检查
|注解 |支持Java类型 |说明 |备注
|@Future |Date、Calendar、Instant、LocalDate、LocalDateTime、LocalTime、MonthDay、OffsetDateTime、OffsetTime、Year、YearMonth、ZonedDateTime、HijrahDate、JapaneseDate、MinguoDate、ThaiBuddhistDate|验证日期为当前时间之后|为null有效
|@FutureOrPresent|Date、Calendar、Instant、LocalDate、LocalDateTime、LocalTime、MonthDay、OffsetDateTime、OffsetTime、Year、YearMonth、ZonedDateTime、HijrahDate、JapaneseDate、MinguoDate、ThaiBuddhistDate|验证日期为当前时间或之后|为null有效
|@Past |Date、Calendar、Instant、LocalDate、LocalDateTime、LocalTime、MonthDay、OffsetDateTime、OffsetTime、Year、YearMonth、ZonedDateTime、HijrahDate、JapaneseDate、MinguoDate、ThaiBuddhistDate|验证日期为当前时间之前|为null有效
|@PastOrPresent |Date、Calendar、Instant、LocalDate、LocalDateTime、LocalTime、MonthDay、OffsetDateTime、OffsetTime、Year、YearMonth、ZonedDateTime、HijrahDate、JapaneseDate、MinguoDate、ThaiBuddhistDate|验证日期为当前时间或之前|为null有效
5. 数值检查
|注解 |支持Java类型 |说明 |备注
|@Max |BigDecimal、BigInteger,byte、short、int、long以及包装类 |小于或等于 |为null有效
|@Min |BigDecimal、BigInteger,byte、short、int、long以及包装类 |大于或等于 |为null有效
|@DecimalMax |BigDecimal、BigInteger、CharSequence,byte、short、int、long以及包装类 |小于或等于 |为null有效
|@DecimalMin |BigDecimal、BigInteger、CharSequence,byte、short、int、long以及包装类 |大于或等于 |为null有效
|@Negative |BigDecimal、BigInteger,byte、short、int、long、float、double以及包装类 |负数 |为null有效,0无效
|@NegativeOrZero|BigDecimal、BigInteger,byte、short、int、long、float、double以及包装类 |负数或零 |为null有效
|@Positive |BigDecimal、BigInteger,byte、short、int、long、float、double以及包装类 |正数 |为null有效,0无效
|@PositiveOrZero|BigDecimal、BigInteger,byte、short、int、long、float、double以及包装类 |正数或零 |为null有效
|@Digits(integer = 3, fraction = 2)|BigDecimal、BigInteger、CharSequence,byte、short、int、long以及包装类|整数位数和小数位数上限|为null有效
6. 其他
|注解 |支持Java类型 |说明 |备注
|@Pattern |CharSequence |匹配指定的正则表达式 |为null有效
|@Email |CharSequence |邮箱地址 |为null有效,默认正则 '.*'
|@Size |CharSequence、Collection、Map、Array |大小范围(length/size>0) |为null有效
|@Length |String |字符串长度范围 |
|@Range |数值类型和String |指定范围 |
|@URL | |URL地址验证 |
7.自定义注解
- 创建约束注解
- 实现一个验证器
- 定义默认的错误信息
@Documented
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
@Constraint(validatedBy = {MobileValidator.class})
@Retention(RUNTIME)
@Repeatable(Mobile.List.class)
public @interface Mobile {
/**
* 错误提示信息,可以写死,也可以填写国际化的key
*/
String message() default "手机号码不正确";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
String regexp() default "^1([38][0-9]|4[579]|5[0-3,5-9]|6[6]|7[0135678]|9[89])\\d{8}$";
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
@Retention(RUNTIME)
@Documented
@interface List {
Mobile[] value();
}
}
@Repeatable注解和List定义可以让该注解在同一个位置重复多次,通常是不同的配置(比如不同的分组和消息)
@Constraint(validatedBy = {MobileValidator.class})该注解是指明我们的自定义约束的验证器,那下面就看一下验证器的写法,需要实现javax.validation.ConstraintValidator接口
||message 错误提示信息,可以写死,也可以填写国际化的key
||groups 分组信息,允许指定此约束所属的验证组
||payload 有效负载,可以通过payload来标记一些需要特殊处理的操作
public class MobileValidator implements ConstraintValidator<Mobile, String> {
/**
* 手机验证规则
*/
private Pattern pattern;
@Override
public void initialize(Mobile mobile) {
pattern = Pattern.compile(mobile.regexp());
}
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
if (value == null) {
return true;
}
return pattern.matcher(value).matches();
}
}
ConstraintValidator接口定义了在实现中设置的两个类型参数。第一个指定要验证的注解类(如Mobile),第二个指定验证器可以处理的元素类型(如String);initialize()方法可以访问约束注解的属性值;isValid()方法用于验证,返回true表示验证通过
@Mobile String mobile
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
@Target({FIELD})
@Retention(RUNTIME)
@Documented
@Constraint(validatedBy = {DateValidator.DateValidatorInner.class})
public @interface DateValidator {
/**
* 必须的属性
* 显示 校验信息
* 利用 {} 获取 属性值,参考了官方的message编写方式
*@see org.hibernate.validator 静态资源包里面 message 编写方式
*/
String message() default "日期格式不匹配{dateFormat}";
/**
* 必须的属性
* 用于分组校验
*/
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
/**
* 非必须
*/
String dateFormat() default "yyyy-MM-dd HH:mm:ss";
/**
* 必须实现 ConstraintValidator接口
*/
class DateValidatorInner implements ConstraintValidator<DateValidator, String> {
private String dateFormat;
@Override
public void initialize(DateValidator constraintAnnotation) {
this.dateFormat = constraintAnnotation.dateFormat();
}
/**
* 校验逻辑的实现
* @param value 需要校验的 值
* @return 布尔值结果
*/
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
if (value == null) {
return true;
}
if("".equals(value)){
return true;
}
try {
Date date = DateUtils.parseDate(value, dateFormat);
return date != null;
} catch (ParseException e) {
return false;
}
}
}
}
@DateValidator(dateFormat = "yyyy-MM-dd")
private String birthday;
6.配置Validator
@Configuration
public class ValidatorConfig {
@Bean
public Validator validator() {
ValidatorFactory validatorFactory = Validation.byProvider(HibernateValidator.class)
.configure()
.failFast(true)
.buildValidatorFactory();
return validatorFactory.getValidator();
}
}
可以通过方法 failFast(true) 或 addProperty("hibernate.validator.fail_fast", "true")设置为快速失败模式,快速失败模式在校验过程中,当遇到第一个不满足条件的参数时就立即返回,不再继续后面参数的校验。否则会一次性校验所有参数,并返回所有不符合要求的错误信息