小言_互联网的博客

SpringBoot+@Validated实现参数验证(非空、类型、范围、格式等)-若依前后端导入Excel数据并校验为例

2974人阅读  评论(0)

场景

若依管理系统前后端分离版基于ElementUI和SpringBoot怎样实现Excel导入和导出:

若依管理系统前后端分离版基于ElementUI和SpringBoot怎样实现Excel导入和导出_霸道流氓气质的博客-CSDN博客

SpringBoot+Vue实现excel导入带格式化的时间参数(moment格式化明天日期并设置el-date-picker默认值):

SpringBoot+Vue实现excel导入带格式化的时间参数(moment格式化明天日期并设置el-date-picker默认值)_霸道流氓气质的博客-CSDN博客

在上面搭建SpringBoot+Vue并实现Excel导入的基础上,为避免导入的excel中数据格式不规范

导致产生大量脏数据,所以需要对excel的数据进行校验,比如不能为空、类型是否为数字、数字范围等等规则。

若依官方对这块有专门说明

后台手册 | RuoYi

 

说明比较简单,具体实现流程如下,也可参考其用户导入的实现代码和流程。

 

这里对用户账号做了非空校验,需要在实体类上对应属性添加


  
  1.     @Xss(message = "用户账号不能包含脚本字符")
  2.     @NotBlank(message = "用户账号不能为空")
  3.     @Size(min = 0, max = 30, message = "用户账号长度不能超过30个字符")
  4.     public String getUserName()
  5.     {
  6.         return userName;
  7.     }

查看@NotBlank注解的实现

 

发现其来自javax.validation。

注:

博客:
霸道流氓气质的博客_CSDN博客-C#,架构之路,SpringBoot领域博主
关注公众号
霸道的程序猿
获取编程相关电子书、教程推送与免费下载。

实现

后台实现流程

Hibernate Validator 是 Bean Validation 的参考实现 。Hibernate Validator 提供了 JSR 303 规范中所有内置 constraint 的实现,

除此之外还有一些附加的 constraint 在日常开发中,Hibernate Validator经常用来验证bean的字段,基于注解,方便快捷高效。

在SpringBoot中可以使用@Validated,注解Hibernate Validator加强版,也可以使用@Valid原来Bean Validation java版本

添加pom依赖


  
  1.         <!-- 自定义验证注解 -->
  2.         <dependency>
  3.             <groupId>org.springframework.boot </groupId>
  4.             <artifactId>spring-boot-starter-validation </artifactId>
  5.         </dependency>

若依框架添加在common模块中

 

使用 Validation API 进行参数效验步骤整个过程如下图所示,用户访问接口,然后进行参数效验 ,

如果效验通过,则进入业务逻辑,否则抛出异常,交由全局异常处理器进行处理

 

 

自定义全局捕获异常


  
  1. /**
  2.  * 全局异常处理器
  3.  *
  4.  * @author ruoyi
  5.  */
  6. @RestControllerAdvice
  7. public class GlobalExceptionHandler
  8. {
  9.     private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);
  10.     /**
  11.      * 自定义验证异常
  12.      */
  13.     @ExceptionHandler(BindException.class)
  14.     public AjaxResult handleBindException (BindException e)
  15.     {
  16.         log.error(e.getMessage(), e);
  17.         String message = e.getAllErrors().get( 0).getDefaultMessage();
  18.         return AjaxResult.error(message);
  19.     }
  20.     /**
  21.      * 自定义验证异常
  22.      */
  23.     @ExceptionHandler(MethodArgumentNotValidException.class)
  24.     public Object handleMethodArgumentNotValidException (MethodArgumentNotValidException e)
  25.     {
  26.         log.error(e.getMessage(), e);
  27.         String message = e.getBindingResult().getFieldError().getDefaultMessage();
  28.         return AjaxResult.error(message);
  29.     }
  30. }

若依框架中将其定义在framework模块下

 

@ExceptionHandler用于指定异常处理方法。当与@RestControllerAdvice配合使用时,用于全局处理控制器里的异常。

在需要校验的实体类属性上或者get方法上添加校验规则注解 ,比如下面


  
  1.     @Xss(message = "用户昵称不能包含脚本字符")
  2.     @Size(min = 0, max = 30, message = "用户昵称长度不能超过30个字符")
  3.     public String getNickName( )
  4.     {
  5.         return nickName;
  6.     }
  7.     public void setNickName( String nickName)
  8.     {
  9.         this. nickName = nickName;
  10.     }
  11.     @Xss(message = "用户账号不能包含脚本字符")
  12.     @NotBlank(message = "用户账号不能为空")
  13.     @Size(min = 0, max = 30, message = "用户账号长度不能超过30个字符")
  14.     public String getUserName( )
  15.     {
  16.         return userName;
  17.     }
  18.     public void setUserName( String userName)
  19.     {
  20.         this. userName = userName;
  21.     }
  22.     @Email(message = "邮箱格式不正确")
  23.     @Size(min = 0, max = 50, message = "邮箱长度不能超过50个字符")
  24.     public String getEmail( )
  25.     {
  26.         return email;
  27.     }
  28.     public void setEmail( String email)
  29.     {
  30.         this. email = email;
  31.     }
  32.     @Size(min = 0, max = 11, message = "手机号码长度不能超过11个字符")
  33.     public String getPhonenumber( )
  34.     {
  35.         return phonenumber;
  36.     }

注解参数说明

注解名称 功能
@Xss 检查该字段是否存在跨站脚本工具
@Null 检查该字段为空
@NotNull 不能为null
@NotBlank 不能为空,常用于检查空字符串
@NotEmpty 不能为空,多用于检测list是否size是0
@Max 该字段的值只能小于或等于该值
@Min 该字段的值只能大于或等于该值
@Past 检查该字段的日期是在过去
@Future 检查该字段的日期是否是属于将来的日期
@Email 检查是否是一个有效的email地址
@Pattern(regex=,flag=) 被注释的元素必须符合指定的正则表达式
@Range(min=,max=,message=) 被注释的元素必须在合适的范围内
@Size(min=, max=) 检查该字段的size是否在min和max之间,可以是字符串、数组、集合、Map等
@Length(min=,max=) 检查所属的字段的长度是否在min和max之间,只能用于字符串
@AssertTrue 用于boolean字段,该字段只能为true
@AssertFalse 该字段的值只能为false

其它格式校验的注解可自行搜索,用法较多。

如果是在Controller中传参时进行校验,可以直接添加注解@Validated

比如添加用户的Controller


  
  1.     @PreAuthorize( "@ss.hasPermi('system:user:add')")
  2.     @Log(title = "用户管理", businessType = BusinessType.INSERT)
  3.     @PostMapping
  4.     public AjaxResult add( @Validated @RequestBody SysUser user)
  5.     {
  6.     }

但是形如Excel导入时,Controller接收的是MultipartFile数据,无法在Controller添加校验


  
  1.     @PostMapping( "/importData")
  2.     public AjaxResult importData( MultipartFile file, String planDateString) throws Exception {
  3.         ExcelUtil< LimitQuotaStatistics> util = new ExcelUtil< LimitQuotaStatistics>( LimitQuotaStatistics.class);
  4.         List< LimitQuotaStatistics> limitQuotaStatisticsList = util.importExcel(file.getInputStream());
  5.         String message =limitQuotaStatisticsService.insertLimitQuotaStatisticsBatch(limitQuotaStatisticsList,planDateString);
  6.         return success(message);
  7.     }

就需要在serviceImpl中通过如下方式进行注入和使用

先通过


  
  1.     @Autowired
  2.     protected Validator validator;

注入依赖

再通过

  BeanValidators.validateWithException(validator, limitQuotaStatistics);

进行参数校验


  
  1. @Service
  2. public class LimitQuotaStatisticsServiceImpl implements ILimitQuotaStatisticsService {
  3.     private static final Logger log = LoggerFactory.getLogger(LimitQuotaStatisticsServiceImpl.class);
  4.     @Autowired
  5.     private LimitQuotaStatisticsMapper limitQuotaStatisticsMapper;
  6.     @Autowired
  7.     protected Validator validator;
  8.     @Override
  9.     public String insertLimitQuotaStatisticsBatch (List<LimitQuotaStatistics> limitQuotaStatisticsList, String planDateString) throws ParseException {
  10.         if (StringUtils.isNull(limitQuotaStatisticsList) || limitQuotaStatisticsList.size() == 0) {
  11.             throw new ServiceException( "导入数据不能为空!");
  12.         }
  13.         int successNum = 0;
  14.         int failureNum = 0;
  15.         StringBuilder successMsg = new StringBuilder();
  16.         StringBuilder failureMsg = new StringBuilder();
  17.         Date plaeDate = new SimpleDateFormat( "yyyy-MM-dd").parse(planDateString);
  18.         for (LimitQuotaStatistics limitQuotaStatistics : limitQuotaStatisticsList) {
  19.             try {
  20.                 BeanValidators.validateWithException(validator, limitQuotaStatistics);
  21.                 limitQuotaStatistics.setPlanDate(plaeDate);
  22.                 this.insertLimitQuotaStatistics(limitQuotaStatistics);
  23.                 successNum++;
  24.                 successMsg.append( "<br/>" + successNum + "、" + limitQuotaStatistics.getDeptName() + " 导入成功");
  25.             } catch (Exception e) {
  26.                 failureNum++;
  27.                 String msg = "<br/>" + failureNum + "、" + limitQuotaStatistics.getDeptName() + " 导入失败:";
  28.                 failureMsg.append(msg + e.getMessage());
  29.                 log.error(msg, e);
  30.             }
  31.         }
  32.         if (failureNum > 0) {
  33.             failureMsg.insert( 0, "很抱歉,导入失败!共 " + failureNum + " 条数据格式不正确,错误如下:");
  34.             throw new ServiceException(failureMsg.toString());
  35.         } else {
  36.             successMsg.insert( 0, "恭喜您,数据已全部导入成功!共 " + successNum + " 条,数据如下:");
  37.         }
  38.         return successMsg.toString();
  39.     }
  40. }

其中BeanValidators的实现如下


  
  1. package com. bdtd. limit. common. utils. bean;
  2. import java. util. Set;
  3. import javax. validation. ConstraintViolation;
  4. import javax. validation. ConstraintViolationException;
  5. import javax. validation. Validator;
  6. /**
  7.  * bean对象属性验证
  8.  *
  9.  * @author ruoyi
  10.  */
  11. public class BeanValidators
  12. {
  13.     public static void validateWithException( Validator validator, Object object, Class<?>... groups)
  14.             throws ConstraintViolationException
  15.     {
  16.         Set< ConstraintViolation< Object>> constraintViolations = validator. validate( object, groups);
  17.         if (!constraintViolations. isEmpty())
  18.         {
  19.             throw new ConstraintViolationException(constraintViolations);
  20.         }
  21.     }
  22. }

这里要校验的实体类添加规则为


  
  1. @Data
  2. @AllArgsConstructor
  3. @NoArgsConstructor
  4. @Builder
  5. public class LimitQuotaStatistics extends BaseEntity
  6. {
  7.     private static final long serialVersionUID = 1L;
  8.     /** id */
  9.     private Long id;
  10.     /** 部门id */
  11.     private Long deptId;
  12.     /** 部门名称 */
  13.     @Excel(name = "部门名称")
  14.     @NotNull(message = "部门名称为空")
  15.     private String deptName;
  16.     /** 夜班人数 */
  17.     @Excel(name = "夜班人数")
  18.     @NotNull(message = "夜班人数为空或格式不对")
  19.     @Range(max = 1000, min = 1, message = "夜班人数需在1-1000之间")
  20.     private Long nightShiftNum;
  21. }

部分字段,仅供参考。

前端页面组件实现

组件实现代码参考若依官方文档中示例


  
  1. <template>
  2.   <!-- 用户导入对话框 -->
  3.   <el-dialog :title="title" :visible.sync="open" width="400px" append-to-body>
  4.     <div class="block">
  5.       <span class="demonstration">计划日期: </span>
  6.       <el-date-picker
  7.         v-model= "planDate"
  8.         type= "date"
  9.         placeholder= "选择计划日期"
  10.         size= "small"
  11.         value-format= "yyyy-MM-dd"
  12.       >
  13.       </el-date-picker>
  14.     </div>
  15.     <br />
  16.     <el-upload
  17.       ref= "upload"
  18.       :limit= "1"
  19.       accept= ".xlsx, .xls"
  20.       :headers= "headers"
  21.       :action= "upLoadUrl + '?planDateString=' + this.planDate"
  22.       :disabled= "isUploading"
  23.       :on-progress= "handleFileUploadProgress"
  24.       :on-success= "handleFileSuccess"
  25.       :auto-upload= "false"
  26.       drag
  27.     >
  28.       <i class="el-icon-upload"> </i>
  29.       <div class="el-upload__text">将文件拖到此处,或 <em>点击上传 </em> </div>
  30.       <div class="el-upload__tip text-center" slot="tip">
  31.         <span>仅允许导入xls、xlsx格式文件。 </span>
  32.       </div>
  33.     </el-upload>
  34.     <div slot="footer" class="dialog-footer">
  35.       <el-button type="primary" @click="submitFileForm">确 定 </el-button>
  36.       <el-button @click="open = false">取 消 </el-button>
  37.     </div>
  38.   </el-dialog>
  39. </template>
  40. <script>
  41. import { getToken } from "@/utils/auth";
  42. import moment from "moment";
  43. export default {
  44.   data( ) {
  45.     return {
  46.       // 是否显示弹出层(用户导入)
  47.       open: false,
  48.       // 弹出层标题(用户导入)
  49.       title: "",
  50.       // 是否禁用上传
  51.       isUploading: false,
  52.       //计划日期
  53.       planDate: new Date(),
  54.       // 设置上传的请求头部
  55.       headers: { Authorization: "Bearer " + getToken() },
  56.       // 上传的地址
  57.       upLoadUrl: "",
  58.     };
  59.   },
  60.   mounted( ) {
  61.     //默认计划日期为明天
  62.     this. planDate = moment(). subtract(- 1, "days"). format( "YYYY-MM-DD");
  63.   },
  64.   methods: {
  65.     /** 导入按钮操作 */
  66.     handleImport( data) {
  67.       this. title = data. title;
  68.       this. upLoadUrl = process. env. VUE_APP_BASE_API + data. upLoadUrl;
  69.       this. open = true;
  70.     },
  71.     // 提交上传文件
  72.     submitFileForm( ) {
  73.       this. $refs. upload. submit();
  74.     },
  75.     // 文件上传中处理
  76.     handleFileUploadProgress( ) {
  77.       this. isUploading = true;
  78.     },
  79.     // 文件上传成功处理
  80.     handleFileSuccess( response) {
  81.       this. open = false;
  82.       this. isUploading = false;
  83.       this. $refs. upload. clearFiles();
  84.       this.$alert(
  85.         "<div style='overflow: auto;overflow-x: hidden;max-height: 70vh;padding: 10px 20px 0;'>" +
  86.           response. msg +
  87.           "</div>",
  88.         "导入结果",
  89.         { dangerouslyUseHTMLString: true }
  90.       );
  91.       //上传数据成功后重新请求数据
  92.       this.$emit( "getList");
  93.     },
  94.   },
  95. };
  96. </script>
  97. <style>
  98. </style>

导入测试效果

 


转载:https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/127871424
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场