Spring boot 结合hibernate-validate校验数据学习
spring boot的web起步依赖中已经引入的hibernate-validate数据校验依赖,只要项目依赖的web起步依赖就无需再引入依赖。
依赖引入
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.1.4.Final</version>
</dependency>
常用注解介绍
validate通过在字段上加入注解来实现一个个指定的校验功能,常用的注解有:
注解 | 描述 |
---|---|
@NotNull | 不能为空 |
@NotBlank | 字符串不能为空也不能为空串 |
@Null | 被校验对象只能为null |
@Size(min=2,max=6) | 被校验字符串长度大小只能在2到6之间 |
@Max(value=7) | 被校验参数最大只能为7 |
@AssertTrue | 标注的属性值必须为true |
@AssertFalse | 标注的属性值必须为false |
@Past | 时间必须是过去的时间 |
@Future | 将来的时间 |
@Pattern | 用于指定一个正则表达式 |
@NotEmpty | 字符串内容非空 |
邮箱 | |
@Range | 用于指定数字,注意是数字的范围,有两个值,min以及max |
@Digits | 内容必须是数字 |
@PositiveOrZero | 0或者整数 |
简单使用
使用validate进行数据的校验可以将校验注解定义在对象字段或者是方法参数中
在pojo中定义校验注解
public class Entity {
@NotBlank(message = "用户名不能为空")
private String username;
@NotBlank(message = "密码不能为空")
@Length(min = 6,message="长度必须大于等于6") //密码长度必须大于六
private String password;
@Email(message="非法邮件地址")
private String email;
@NotBlank(message = "手机号码不能为空")
@Length(min = 11, max = 11, message = "手机号只能为11位")
@Pattern(regexp = "^[1][3,4,5,6,7,8,9][0-9]{9}$", message = "手机号格式有误")
private String phone;
}
在方法参数上定义校验注解
注意,在方法的参数上使用校验注解需要在类名上添加@Validated注解
@RestController
@RequestMapping("/demo")
@Validated //参数校验
public class DemoController {
@PostMapping("/search/{page}/{size}")
public Entity demo(@RequestBody @Validated Entity entity,
@PathVariable("page") @Min(value = 0) Integer page,
@PathVariable("size") @Min(value = 0) Integer size){
System.out.println(entity.getUsername()+"---"+entity.getPassword());
System.out.println(page+"----------"+size);
return entity;
}
}
统一异常处理
若定义对象字段上,则若是校验出数据无效,则会抛出:
MethodArgumentNotValidException extends Exception //方法参数无效异常
若是定义在方法参数中,若数据无效,则抛出:
ConstraintViolationException extends ValidationException //约束异常
我们可以在spring boot中定义一个全局异常处理器,用于拦截校验出现异常后返回的提示信息
/**
* 全局异常处理器
*/
@ControllerAdvice
public class ExceptionGlobalHandler {
@ExceptionHandler(Exception.class)
@ResponseBody
public Result handle(Exception e){
//System.out.println(e);
String message = null;
if(e instanceof MethodArgumentNotValidException){ //参数校验-对象数据校验异常
//获取提示信息
for (ObjectError error : ((MethodArgumentNotValidException) e).getBindingResult().getAllErrors()) {
message = error.getDefaultMessage();//获取自定义的错误提示信息
}
}else if(e instanceof ConstraintViolationException){ //方法参数校验异常
Iterator<ConstraintViolation<?>> iterator = ((ConstraintViolationException) e).getConstraintViolations().iterator();
ConstraintViolation<?> next = iterator.next();
message = next.getMessage(); //获取自定义的错误提示信息
}else{
message = e.getMessage(); //其他异常
}
return Result.error().setMessage(message);
}
}
扩展一:分组校验
在使用hibernate-validate进行数据校验时,我们有时候会进行分情况校验数据,例如:在新增操作时,我们不进行id不为空校验,但是在修改操作时,我们需要进行id不为空的校验。而这,若当当使用@NotNull进行校验,则在insert操作时也会进行为空校验,这不是我们想要的。此时,我们可以对校验进行分组,在不同的情况下使用不同的分组进行数据的校验。
每一组分组都需要一个接口来作为分组的标志,但接口内可以不添加内容
一般的分组有:新增insert;修改ipdate;删除delete;查询select
我是用内部接口来管理这些标记类接口
public class ValidateGroupFalg {
/**
* 新增操作组
*/
public static interface InsertFlag{}
/**
* 删除操作组
*/
public static interface DeleteFlag{}
/**
* 修改操作组
*/
public static interface UpdateFlag{}
/**
* 查询操作组
*/
public static interface SelectFlag{}
}
对于校验用户id,进行分组校验
@TableName("tb_demo")
public class TbDemo implements Serializable {
@TableId(type = IdType.INPUT)
@NotNull(message = "请指定要修改的编号",groups = ValidateGroupFalg.UpdateFlag.class)
private String id;
}
在使用时只需要在接收方法的参数中,添加校验注解,并指定分组标记即可
public Result updateById(@RequestBody @Validated(ValidateGroupFalg.UpdateFlag.class) TbDemo tbDemo){
// ....
}
扩展一:自定义校验
当我们需要进行某些指定的校验时,我们可以通过hibernate-validator提供的AIP创建自定义校验。
步骤
1、创建注解,并指定数据处理类
2、创建数据处理类,实现内部方法
3、使用注解
实现
1、创建注解,并指定数据处理类
/**
* 时间字符串格式校验注解
*/
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = DateTimeValidator.class) //用于指定数据处理类
public @interface DateFormatValidate {
String message() default "时间格式错误";
String format() default "yyyy-MM-dd";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
2、创建数据处理类,实现内部方法
import com.bishe.common.validate.DateFormatValidate;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.text.SimpleDateFormat;
/**
* 时间字符串格式校验
*/
public class DateTimeValidator implements ConstraintValidator<DateFormatValidate, String> {
private DateFormatValidate dateFormatValidate ; //注解引用
public void initialize(DateFormatValidate constraint) {
this.dateFormatValidate = constraint;
//System.out.println("初始化方法调用。。。");
}
//校验方法,成功返回true;失败返回false,validate会进行判断,false抛出异常
public boolean isValid(String value, ConstraintValidatorContext context) {
//System.out.println("开始校验。。。");
if (value == null) {
return true;
}
String format = dateFormatValidate.format(); //获取自定义的时间格式
if (value.length() != format.length()) { //若2020-10-10 与YYYY-mm-dd 长度不同,返回false
return false;
}
try {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(format); //创建时间格式对象
simpleDateFormat.parse(value); //尝试通过格式转化被校验参数,成功返回true,失败,返回false
} catch (Exception e){
return false; //转化失败
}
return true; //校验成功
}
}
3、使用注解
@DateFormatValidate(format = "yyyy/MM/dd", message = "格式错误,正确格式为:yyyy/MM/dd")
private Date birthday;
完成!