文章目录
Hibernate Validator:一款优雅的验证框架
1、Hibernate Validator的简单介绍
平常的项目当中,无论是从controller接受传递来的数据,还是内部方法接口的形参赋值,都离不开数据的校验。这些校验有着实际的业务需求,例如你定义了一个价格,它肯定是非负数,但是前端传来一个负数,是不是需要校验避免把错误的数据写进数据库呢?但是在平常的项目开发中,这些校验的编写,存在于我们的业务逻辑当中,使我们的代码变得臃肿复杂。
有没有一款框架能够简便校验的编写呢?Hibernate Validator就是为了解决这个问题的。只要加上注解,我们就可以一行轻松解决校验问题,十分方便。
2、SpringBoot集成Hibernate Validator
2.1、引入依赖
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-validation -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
<version>2.2.2.RELEASE</version>
</dependency>
2.2、使用Validator和手写验证对比
现在我们写一个简单的小demo,来对比一些手写校验逻辑和使用Hibernate Validator框架,到底简便多少吧!
假设有个添加员工信息需求,前端传来数据,映射到后台的Javabean类是Employee类,也就是员工类。员工类中有四个属性,id就是员工编号,parent_id就是员工对应的部门编号,name对应员工的名字,createtime表示员工的入职时间。
Employee类有以下要求:
1、前端传入的id必须为空,因为id在数据库里是自增字段,不支持手动赋值。
2、parent_id不能为空,每个员工必须有一个部门编号。
3、name也不能为空。
4、createtime时间必须小于当前查询的系统时间。
Employee类的具体属性
public class Employee {
private Integer id;
private Integer parent_id;
private String name;
private LocalDateTime createtime;
}
那现在我们用普通的方式,对controller获得数据进行校验
@RestController
public class TestController {
@PostMapping("/add")
public Object add(@RequestBody Employee employee){
String msg="";
boolean flag=false;
if(employee.getId()!=null){
msg += "id必须为空";flag=true;
}
if(employee.getParent_id()==null){
msg += "parent_id不能为空";flag=true;
}
if(employee.getName()==null){
msg += "name不能为空";flag=true;
}
if(employee.getCreatetime().isAfter(LocalDateTime.now())){
msg += "createtime必须在当前时间之前";flag=true;
}
if(flag==true){
return msg;
}
return "ok";
}
}
我们可以看到在正式的业务逻辑之前,只是校验数据代码就很多行了,如果存在多个controller,这些校验代码还要重复写,使得整个项目的代码变得臃肿起来。
现在我们使用Hibernate Validator来编写校验逻辑。
1、在Employee类上加上具体的约束注解。
public class Employee {
@Null
private Integer id;
@NotNull
private Integer parent_id;
@NotBlank
private String name;
@PastOrPresent
private LocalDateTime createtime;
}
2、在controller的类上加上@Validated注解,标注这个类需要校验。在需要校验的参数前面加上@Valid注解,使这个实体类的属性得到校验。
@RestController
@Validated
public class TestController2 {
@PostMapping("/add")
public Object add(@RequestBody @Valid Employee employee){
return "ok";
}
}
当前端传来不合规范的数据时,控制台就会报错,并显示对应的错误信息。这样看来,使用validator来进行数据校验,是不是简洁很多呢?
3、Validator的全局异常捕获
当输入前端输入的数据不规范时,控制台就会报错,那我们怎么把错误的信息,返回把前端页面显示呢?这时候就要用到异常捕获。我们可以定义一个全局异常的捕获类,当出现某种异常时,就捕获它,对数据进行封装,返回到前端。
GlobalExceptionHandler.java(全局捕获类)
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler
public Object expHandler(Exception e){
if (e instanceof MethodArgumentNotValidException){
log.error(e.getMessage());
String msg="";
List<ObjectError> errors=((MethodArgumentNotValidException) e).getBindingResult().getAllErrors();
for(ObjectError error : errors){
msg += error.getDefaultMessage();
msg += ",";
}
return msg;
}
return null;
}
}
4、Validator的分组验证
Hibernate Validator可以用在JavaBean的数据校验上,但是这样可能会产生一个问题:一个Javabean对应的多个controller,不同的controller对于实体类的约束都不一样。例如当前端更新数据的时候,它可能需要A属性为空,但是当前端新增数据的时候,它可能需要A属性不为空。这就涉及分组了。
1、首先在分组的实体类中定义组别接口进行区分,然后对应分组的注解后面添上组别接口,如果不标注则代表是默认组别。
public class Employee {
@Positive(message = "id必须为正数",groups = {Employee.add.class})
private int id;
@Positive(message = "parent_id必须为正数",groups = {Employee.add.class})
private int parent_id;
@NotBlank(message = "name不能为空")
private String name;
@NotNull(message = "createtime不能为空")
@PastOrPresent
private LocalDateTime createtime;
@Valid
private List<Department> departments;
public interface add{};
public interface update{};
}
2、因为@Valid注解是不可以区别组别的,所以我们改用@Validated注解,后面跟上组别区分。
@RestController
@Validated
public class Testcontroller {
@PostMapping("/add")
public Object add(@RequestBody @Validated({Employee.add.class}) Employee employee){
return "add ok";
}
@PostMapping("/update")
public Object update(@RequestBody @Validated({Default.class}) Employee employee){
return "update ok";
}
}