SpringMVC使用hibernate-validator进行参数校验最佳实践记录

作者:kusedexingfu 时间:2022-11-12 23:44:04 

在我们用Controller接收参数后,往往需要对参数进行校验。如果我们手写校验的话,就会有一堆的判空代码,看起来很不优雅,写起来也费时费力。下面来看下通过hibernate-validator来进行优雅的参数校验。

首先需要引入依赖:


<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.2.0.Final</version>
</dependency>

hibernate-validator注解

hibernate-validator提供了很多注解来让我们进行参数校验:

常用的校验注解如下表所示:

注解说明
@Null被注释的元素必须为null
@AssertTrue被注释的元素必须为true
@AssertFalse被注释的元素必须为false
@DecimalMin(value=,message=)被注释的元素必须是一个数字,其值必须大于等于指定的最小值
@DecimalMax(value=,message=)被注释的元素必须是一个数字,其值必须小于等于指定的最大值
@Digits (integer, fraction)被注释的元素必须是一个数字,其值必须在可接受的范围内
@Past被注释的元素必须是一个过去的日期
@Future被注释的元素必须是一个将来的日期
@Email被注释的元素必须是电子邮箱地址

主要区分下@NotNull、@NotEmpty、@NotBlank 3个注解的区别:
(1)@NotNull:任何对象的value不能为null。
(2)@NotEmpty:集合对象的元素不为0,即集合不为空,也可以用于字符串不为null。
(3)@NotBlank:只能用于字符串不为null,并且字符串trim()以后length要大于0。

需要注意如下几点:
(1)除了@Empty要求字符串不能全是空格,其他的字符串校验都是允许空格的。
(2)message是可以引用常量的,但是如@Size里max不允许引用对象常量,基本类型常量是可以的。message是错误提示信息,是可以返回给前台的。
(3)大部分规则校验都是允许参数为null,即当不存在这个值时,就不进行校验了。


不太常用的校验注解如下表所示:

注解说明
@Null被注释的元素必须为null
@AssertTrue被注释的元素必须为true
@AssertFalse被注释的元素必须为false
@DecimalMin(value=,message=)被注释的元素必须是一个数字,其值必须大于等于指定的最小值
@DecimalMax(value=,message=)被注释的元素必须是一个数字,其值必须小于等于指定的最大值
@Digits (integer, fraction)被注释的元素必须是一个数字,其值必须在可接受的范围内
@Past被注释的元素必须是一个过去的日期
@Future被注释的元素必须是一个将来的日期
@Email被注释的元素必须是电子邮箱地址

代码实战

1.、使用BindingResult获取检验结果

我们可以使用BindingResult获取检验结果,构造友好的返回信息

Controller中的代码如下:


@Api(tags = "校验框架")
@RestController
@RequestMapping("/validate")
public class ValidatedController {

@ApiOperation(value = "bindValidate")
   @PostMapping("bindValidate")
   public ValidatedVO bindValidate(@RequestBody @Validated(value= {ValidatedGroup.DELET.class}) ValidatedVO validatedVO, BindingResult result) {
       if (result.hasErrors()) {
           StringBuilder message = new StringBuilder("参数校验失败:");
           List<ObjectError> errors = result.getAllErrors();
           for (ObjectError error : errors) {
               message.append(error.getDefaultMessage()).append(".");
           }
           throw HttpError.error(111, message.toString());
       }
       return validatedVO;
   }

}

我们用以下实体类来作为Controller的参数接收实体


import lombok.Data;

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;

@Data
public class ValidatedVO {

//更新、删除时不能为空
   @NotNull(message = "id不能为空", groups = {ValidatedGroup.UPDATE.class, ValidatedGroup.DELET.class})
   private Long id;

//新增、更新时不能为空
   @NotBlank(message = "name不能为空", groups = {ValidatedGroup.CREATE.class ,ValidatedGroup.UPDATE.class})
   private String name;

//查询时不能为空
   @NotBlank(message = "queryParam不能为空", groups = {ValidatedGroup.QUERY.class})
   private String queryParam;

}

分组的类:


public class ValidatedGroup {

public interface CREATE{}

public interface DELET{}

public interface UPDATE{}

public interface QUERY{}
}

需要注意的是,@Validated支持分组校验,即校验注解中的groups属性。这个为我们的校验也提供了便利。我们可以在Controller需要校验的参数前用@Validated的value属性来表示需要校验的分组,那么就会只会校验实体对象中拥有相同的分组的属性。这样我们就可以只用一个实体满足不同的场景了。

启动项目后,我们在swagger上请求,传递空的json字符串【{}】,返回结果:

{
  "code": 111,
  "message": "参数校验失败:id不能为空."
}

 然而每次都需要在请求进来时用BindingResult做处理,很不优雅。

2.通过@RestControllerAdvice统一处理参数校验信息

如果我们不用BindingResult获取校验结果,即不做处理,框架就会抛出异常,响应400码,返回一些不友好的错误信息。即用如下代码:


@ApiOperation(value = "bindValidate")
   @PostMapping("bindValidate")
   public ValidatedVO bindValidate(@RequestBody @Validated(value= {ValidatedGroup.DELET.class}) ValidatedVO validatedVO/*, BindingResult result*/) {
       /*if (result.hasErrors()) {
           StringBuilder message = new StringBuilder("参数校验失败:");
           List<ObjectError> errors = result.getAllErrors();
           for (ObjectError error : errors) {
               message.append(error.getDefaultMessage()).append(".");
           }
           throw HttpError.error(111, message.toString());
       }*/
       return validatedVO;
   }

那么我们就可以使用@RestControllerAdvice来对异常进行处理,进行友好信息的提示

如果对@RestControllerAdvice和@ControllerAdvice不了解的,可以去查询学习,这里不进行讲述


@RestControllerAdvice
public class GlobalExceptionHandler {    

@ExceptionHandler(Exception.class)
   public CommonErrorVO handle(Exception e){
       if (e instanceof HttpError) {
           HttpError error = (HttpError)e;
           return CommonErrorVO.builder().code(error.getCode()).message(error.getMessage()).build();
       } else if (e instanceof MethodArgumentNotValidException) {
           MethodArgumentNotValidException validException = (MethodArgumentNotValidException) e;
           StringBuilder message = new StringBuilder("");
           validException.getBindingResult().getAllErrors().forEach(err -> {
               message.append(err.getDefaultMessage()).append(".");
           });
           return CommonErrorVO.builder().code(888).message(message.toString()).build();
       } else {
           return CommonErrorVO.builder().code(999).message(e.getMessage()).build();
       }
   }    
}

MethodArgumentNotValidException异常即为hibernate-validator校验不通过抛出的异常信息,我们从其中获取到校验失败的信息来进行组装。

如上,我们通过@Validated配合@RestControllerAdvice完成了优雅的参数校验。

为了体现分组校验的便利性,我做了如下的测试,如果每个请求参数都是空的json字符串【{}】的话,那么返回的信息如下。


@Api(tags = "校验框架")
@RestController
@RequestMapping("/validate")
public class ValidatedController {

/**
    * @Validated注解表示开启Spring的校验机制,支持分组校验,声明在入参上。
    * @param validatedVO
    * @return
    */
   @ApiOperation(value = "分组校验:QUERY参数校验")//返回 【queryParam不能为空】
   @PostMapping("queryValidate")
   public ValidatedVO queryValidate(@RequestBody @Validated(value= {ValidatedGroup.QUERY.class}) ValidatedVO validatedVO) {
       return validatedVO;
   }

@ApiOperation(value = "分组校验:CREATE参数校验")// 返回 【name不能为空】
   @PostMapping("createValidate")
   public ValidatedVO createValidate(@RequestBody @Validated(value= {ValidatedGroup.CREATE.class}) ValidatedVO validatedVO) {
       return validatedVO;
   }

@ApiOperation(value = "分组校验:UPDATE参数校验") // 返回【id不能为空.name不能为空.】
   @PostMapping("updateValidate")
   public ValidatedVO updateValidate(@RequestBody @Validated(value= {ValidatedGroup.UPDATE.class}) ValidatedVO validatedVO) {
       return validatedVO;
   }

@ApiOperation(value = "分组校验:DELETE参数校验") //返回 【id不能为空】
   @PostMapping("deleteValidate")
   public ValidatedVO deleteValidate(@RequestBody @Validated(value= {ValidatedGroup.DELET.class}) ValidatedVO validatedVO) {
       return validatedVO;
   }
}

来源:https://blog.csdn.net/kusedexingfu/article/details/116676271

标签:SpringMVC,hibernate,validator,参数校验
0
投稿

猜你喜欢

  • C#修改IIS站点framework版本号的方法

    2023-08-29 19:14:13
  • C#在Windows窗体控件实现内容拖放(DragDrop)功能

    2021-07-25 01:48:27
  • C#表达式目录树示例详解

    2021-10-01 01:52:46
  • java创建以任意图片为背景的窗口

    2021-11-16 03:08:14
  • Android新闻广告条滚动效果

    2023-05-13 10:30:37
  • 浅谈Spring中Bean的作用域、生命周期

    2023-11-14 02:44:21
  • Java MAVEN 工程pom配置报错解决方案

    2023-09-25 18:29:25
  • C# 添加、修改以及删除Excel迷你图表的实现方法

    2023-04-06 22:07:27
  • 在Eclipse中运行Solr 基础知识

    2021-07-06 22:51:04
  • java9中gc log参数迁移

    2022-06-28 03:21:01
  • java基础教程之拼图游戏的实现

    2022-05-02 21:22:21
  • Java超细致讲解数组的使用

    2022-07-20 06:25:16
  • Android中显示GIF动画的实现代码

    2022-01-26 23:36:50
  • Android开发之菜单(menu)用法实例分析

    2022-06-22 23:13:28
  • Android开发实现删除联系人通话记录的方法

    2022-10-26 16:56:49
  • C#实现的JS操作类实例

    2023-11-13 05:19:48
  • Unity实现粒子光效导出成png序列帧

    2023-08-30 21:22:52
  • C# Winform 实现屏蔽键盘的win和alt+F4的实现代码

    2021-07-01 11:48:18
  • c#获取存储过程返回值示例分享

    2021-08-24 18:45:48
  • Java URL自定义私有网络协议

    2021-08-11 02:21:06
  • asp之家 软件编程 m.aspxhome.com