文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Spring Boot参数校验以及分组校验的使用

2024-12-03 00:13

关注

使用这种方式虽然简单直接,但是也有不好的地方,一是降低了开发效率,因为我们需要校验的参数会存在很多地方,并且不同地方会有重复校验,其次降低了代码可读性,因为在业务代码中掺杂了太多额外工作的代码。

所以我们可以使用validator组件来代替我们进行不必要的coding操作。本文基于validator的介绍资料,也结合自己在项目中的实际使用经验进行了总结,希望能帮到大家。

1 什么是validator

Bean Validation是Java定义的一套基于注解的数据校验规范,目前已经从JSR 303的1.0版本升级到JSR 349的1.1版本,再到JSR 380的2.0版本(2.0完成于2017.08),已经经历了三个版本 。需要注意的是,JSR只是一项标准,它规定了一些校验注解的规范,但没有实现,比如@Null、@NotNull、@Pattern等,它们位于 javax.validation.constraints这个包下。而hibernate validator是对这个规范的实现,并增加了一些其他校验注解,如 @NotBlank、@NotEmpty、@Length等,它们位于org.hibernate.validator.constraints这个包下。

如果我们的项目使用了Spring Boot,hibernate validator框架已经集成在 spring-boot-starter-web中,所以无需再添加其他依赖。如果不是Spring Boot项目,需要添加如下依赖。

  1.  
  2.     org.hibernate.validator 
  3.     hibernate-validator 
  4.     6.0.8.Final 
  5.  

 

二 注解介绍

1 validator内置注解

 

 

 

 


 

 

 

注解 说明
@Null 被注释的元素必须为null
@NotNull 被注释的元素不能为null
@AssertTrue 被注释的元素必须为true
@AssertFalse 被注释的元素必须为false
@Min(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值
@Max(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
@DecimalMin(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值
@DecimalMax(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
@Size(max,min) 被注释的元素的大小必须在指定的范围内
@Digits(integer, fraction) 被注释的元素必须是一个数字,其值必须必须在可接受的范围内
@Past 被注释的元素必须是一个过去的日期
@Future 被注释的元素必须是一个将来的日期
@Pattern(value) 被注释的元素必须符合指定的正则表达式

hibernate validator中扩展定义了如下注解:

注解 说明
@NotBlank 被注释的元素不能为null,且长度必须大于0,只能用于注解字符串
@Email 被注释的元素必须是电子邮箱地址
@Length(min=,max=) 被注释的字符串的大小必须在指定的范围内
@NotEmpty 被注释的元素值不为null且不为空,支持字符串、集合、Map和数组类型
@Range 被注释的元素必须在规定的范围内

三 使用

使用起来比较简单,都是使用注解方式使用。具体来说分为单参数校验、对象参数校验,单参数校验就是controller接口按照单参数接收前端传值,没有封装对象进行接收,如果有封装对象那就是对象参数校验。

1 单参数校验

单参数校验只需要在参数前添加注解即可,如下所示:

  1. public Result deleteUser(@NotNull(message = "id不能为空") Long id) { 
  2.   // do something 

但有一点需要注意,如果使用单参数校验,controller类上必须添加@Validated注解,如下所示:

  1. @RestController 
  2. @RequestMapping("/user"
  3. @Validated // 单参数校验需要加的注解 
  4. public class UserController { 
  5.   // do something 

2 对象参数校验

对象参数校验使用时,需要先在对象的校验属性上添加注解,然后在Controller方法的对象参数前添加@Validated 注解,如下所示:

  1. public Result addUser(@Validated UserAO userAo) { 
  2.     // do something 
  3.  
  4. public class UserAO { 
  5.   @NotBlank 
  6.   private String name
  7.  
  8.   @NotNull 
  9.   private Integer age; 
  10.    
  11.   …… 

注解分组

在对象参数校验场景下,有一种特殊场景,同一个参数对象在不同的场景下有不同的校验规则。比如,在创建对象时不需要传入id字段(id字段是主键,由系统生成,不由用户指定),但是在修改对象时就必须要传入id字段。在这样的场景下就需要对注解进行分组。

1)组件有个默认分组Default.class, 所以我们可以再创建一个分组UpdateAction.class,如下所示:

  1. public interface UpdateAction { 

2)在参数类中需要校验的属性上,在注解中添加groups属性:

  1. public class UserAO { 
  2.  
  3.     @NotNull(groups = UpdateAction.class, message = "id不能为空"
  4.     private Long id; 
  5.      
  6.     @NotBlank 
  7.     private String name
  8.  
  9.     @NotNull 
  10.     private Integer age; 
  11.      
  12.     …… 

如上所示,就表示只在UpdateAction分组下校验id字段,在默认情况下就会校验name字段和age字段。

然后在controller的方法中,在@Validated注解里指定哪种场景即可,没有指定就代表采用Default.class,采用其他分组就需要显示指定。如下代码便表示在addUser()接口中按照默认情况进行参数校验,在updateUser()接口中按照默认情况和UpdateAction分组对参数进行共同校验。

  1. public Result addUser(@Validated UserAO userAo) { 
  2.   // do something 
  1. public Result updateUser(@Validated({Default.class, UpdateAction.class}) UserAO userAo) { 
  2.   // do something 

对象嵌套

如果需要校验的参数对象中还嵌套有一个对象属性,而该嵌套的对象属性也需要校验,那么就需要在该对象属性上增加@Valid注解。

  1. public class UserAO { 
  2.  
  3.     @NotNull(groups = UpdateAction.class, message = "id不能为空"
  4.     private Long id; 
  5.      
  6.     @NotBlank 
  7.     private String name
  8.  
  9.     @NotNull 
  10.     private Integer age; 
  11.      
  12.     @Valid 
  13.     private Phone phone; 
  14.      
  15.     …… 
  16.  
  17. public class Phone { 
  18.     @NotBlank 
  19.     private String operatorType; 
  20.      
  21.     @NotBlank 
  22.     private String phoneNum; 

3 错误消息的捕获

参数校验失败后会抛出异常,我们只需要在全局异常处理类中捕获参数校验的失败异常,然后将错误消息添加到返回值中即可。捕获异常的方法如下所示,返回值Result是我们系统自定义的返回值类。

  1. @RestControllerAdvice(basePackages= {"com.alibaba.dc.controller","com.alibaba.dc.service"}) 
  2. public class GlobalExceptionHandler { 
  3.  
  4.   @ExceptionHandler(value = {Throwable.class}) 
  5.   Result handleException(Throwable e, HttpServletRequest request){ 
  6.     // 异常处理 
  7.         } 

需要注意的是,如果缺少参数抛出的异常是MissingServletRequestParameterException,单参数校验失败后抛出的异常是ConstraintViolationException,get请求的对象参数校验失败后抛出的异常是BindException,post请求的对象参数校验失败后抛出的异常是MethodArgumentNotValidException,不同异常对象的结构不同,对异常消息的提取方式也就不同。如下图所示:

1)MissingServletRequestParameterException

  1. if(e instanceof MissingServletRequestParameterException){ 
  2.     Result result = Result.buildErrorResult(ErrorCodeEnum.PARAM_ILLEGAL); 
  3.     String msg = MessageFormat.format("缺少参数{0}", ((MissingServletRequestParameterException) e).getParameterName()); 
  4.     result.setMessage(msg); 
  5.     return result; 

2)ConstraintViolationException异常

  1. if(e instanceof ConstraintViolationException){ 
  2.   // 单个参数校验异常 
  3.   Result result = Result.buildErrorResult(ErrorCodeEnum.PARAM_ILLEGAL); 
  4.   Set> sets = ((ConstraintViolationException) e).getConstraintViolations(); 
  5.   if(CollectionUtils.isNotEmpty(sets)){ 
  6.     StringBuilder sb = new StringBuilder(); 
  7.     sets.forEach(error -> { 
  8.                     if (error instanceof FieldError) { 
  9.                         sb.append(((FieldError)error).getField()).append(":"); 
  10.                     } 
  11.                     sb.append(error.getMessage()).append(";"); 
  12.                 }); 
  13.     String msg = sb.toString(); 
  14.     msg = StringUtils.substring(msg, 0, msg.length() -1); 
  15.     result.setMessage(msg); 
  16.   } 
  17.   return result; 

3)BindException异常

  1. if (e instanceof BindException){ 
  2.       // get请求的对象参数校验异常 
  3.       Result result = Result.buildErrorResult(ErrorCodeEnum.PARAM_ILLEGAL); 
  4.       List errors = ((BindException) e).getBindingResult().getAllErrors(); 
  5.       String msg = getValidExceptionMsg(errors); 
  6.       if (StringUtils.isNotBlank(msg)){ 
  7.         result.setMessage(msg); 
  8.       } 
  9.       return result; 
  1. private String getValidExceptionMsg(List errors) { 
  2.   if(CollectionUtils.isNotEmpty(errors)){ 
  3.     StringBuilder sb = new StringBuilder(); 
  4.     errors.forEach(error -> { 
  5.                     if (error instanceof FieldError) { 
  6.                        sb.append(((FieldError)error).getField()).append(":"); 
  7.                     } 
  8.                     sb.append(error.getDefaultMessage()).append(";"); 
  9.                 }); 
  10.     String msg = sb.toString(); 
  11.     msg = StringUtils.substring(msg, 0, msg.length() -1); 
  12.     return msg; 
  13.   } 
  14.   return null

4)MethodArgumentNotValidException异常

  1. if (e instanceof MethodArgumentNotValidException){ 
  2.       // post请求的对象参数校验异常 
  3.       Result result = Result.buildErrorResult(ErrorCodeEnum.PARAM_ILLEGAL); 
  4.       List errors = ((MethodArgumentNotValidException) e).getBindingResult().getAllErrors(); 
  5.       String msg = getValidExceptionMsg(errors); 
  6.       if (StringUtils.isNotBlank(msg)){ 
  7.         result.setMessage(msg); 
  8.       } 
  9.       return result; 

【本文为51CTO专栏作者“阿里巴巴官方技术”原创稿件,转载请联系原作者】

戳这里,看该作者更多好文

 

来源:51CTO专栏内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯