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

news2025/7/9 19:58:05

场景

若依管理系统前后端分离版基于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

 

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

 

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

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

查看@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依赖

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

若依框架添加在common模块中

 

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

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

 

 

自定义全局捕获异常

/**
 * 全局异常处理器
 *
 * @author ruoyi
 */
@RestControllerAdvice
public class GlobalExceptionHandler
{
    private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);

    /**
     * 自定义验证异常
     */
    @ExceptionHandler(BindException.class)
    public AjaxResult handleBindException(BindException e)
    {
        log.error(e.getMessage(), e);
        String message = e.getAllErrors().get(0).getDefaultMessage();
        return AjaxResult.error(message);
    }

    /**
     * 自定义验证异常
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Object handleMethodArgumentNotValidException(MethodArgumentNotValidException e)
    {
        log.error(e.getMessage(), e);
        String message = e.getBindingResult().getFieldError().getDefaultMessage();
        return AjaxResult.error(message);
    }
}

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

 

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

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

    @Xss(message = "用户昵称不能包含脚本字符")
    @Size(min = 0, max = 30, message = "用户昵称长度不能超过30个字符")
    public String getNickName()
    {
        return nickName;
    }

    public void setNickName(String nickName)
    {
        this.nickName = nickName;
    }

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

    public void setUserName(String userName)
    {
        this.userName = userName;
    }

    @Email(message = "邮箱格式不正确")
    @Size(min = 0, max = 50, message = "邮箱长度不能超过50个字符")
    public String getEmail()
    {
        return email;
    }

    public void setEmail(String email)
    {
        this.email = email;
    }

    @Size(min = 0, max = 11, message = "手机号码长度不能超过11个字符")
    public String getPhonenumber()
    {
        return phonenumber;
    }

注解参数说明

注解名称功能
@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

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

    }

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

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

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

先通过

    @Autowired
    protected Validator validator;

注入依赖

再通过

  BeanValidators.validateWithException(validator, limitQuotaStatistics);

进行参数校验

@Service
public class LimitQuotaStatisticsServiceImpl implements ILimitQuotaStatisticsService {

    private static final Logger log = LoggerFactory.getLogger(LimitQuotaStatisticsServiceImpl.class);

    @Autowired
    private LimitQuotaStatisticsMapper limitQuotaStatisticsMapper;

    @Autowired
    protected Validator validator;


    @Override
    public String insertLimitQuotaStatisticsBatch(List<LimitQuotaStatistics> limitQuotaStatisticsList, String planDateString) throws ParseException {

        if (StringUtils.isNull(limitQuotaStatisticsList) || limitQuotaStatisticsList.size() == 0) {
            throw new ServiceException("导入数据不能为空!");
        }
        int successNum = 0;
        int failureNum = 0;
        StringBuilder successMsg = new StringBuilder();
        StringBuilder failureMsg = new StringBuilder();

        Date plaeDate = new SimpleDateFormat("yyyy-MM-dd").parse(planDateString);

        for (LimitQuotaStatistics limitQuotaStatistics : limitQuotaStatisticsList) {
            try {
                BeanValidators.validateWithException(validator, limitQuotaStatistics);
                limitQuotaStatistics.setPlanDate(plaeDate);
                this.insertLimitQuotaStatistics(limitQuotaStatistics);
                successNum++;
                successMsg.append("<br/>" + successNum + "、" + limitQuotaStatistics.getDeptName() + " 导入成功");

            } catch (Exception e) {
                failureNum++;
                String msg = "<br/>" + failureNum + "、" + limitQuotaStatistics.getDeptName() + " 导入失败:";
                failureMsg.append(msg + e.getMessage());
                log.error(msg, e);
            }
        }
        if (failureNum > 0) {
            failureMsg.insert(0, "很抱歉,导入失败!共 " + failureNum + " 条数据格式不正确,错误如下:");
            throw new ServiceException(failureMsg.toString());
        } else {
            successMsg.insert(0, "恭喜您,数据已全部导入成功!共 " + successNum + " 条,数据如下:");
        }
        return successMsg.toString();
    }
}

其中BeanValidators的实现如下

package com.bdtd.limit.common.utils.bean;

import java.util.Set;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.Validator;

/**
 * bean对象属性验证
 *
 * @author ruoyi
 */
public class BeanValidators
{
    public static void validateWithException(Validator validator, Object object, Class<?>... groups)
            throws ConstraintViolationException
    {
        Set<ConstraintViolation<Object>> constraintViolations = validator.validate(object, groups);
        if (!constraintViolations.isEmpty())
        {
            throw new ConstraintViolationException(constraintViolations);
        }
    }
}

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

@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class LimitQuotaStatistics extends BaseEntity
{
    private static final long serialVersionUID = 1L;

    /** id */
    private Long id;

    /** 部门id */
    private Long deptId;

    /** 部门名称 */
    @Excel(name = "部门名称")
    @NotNull(message = "部门名称为空")
    private String deptName;

    /** 夜班人数 */
    @Excel(name = "夜班人数")
    @NotNull(message = "夜班人数为空或格式不对")
    @Range(max = 1000, min = 1, message = "夜班人数需在1-1000之间")
    private Long nightShiftNum;
}

部分字段,仅供参考。

前端页面组件实现

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

<template>
  <!-- 用户导入对话框 -->
  <el-dialog :title="title" :visible.sync="open" width="400px" append-to-body>
    <div class="block">
      <span class="demonstration">计划日期: </span>
      <el-date-picker
        v-model="planDate"
        type="date"
        placeholder="选择计划日期"
        size="small"
        value-format="yyyy-MM-dd"
      >
      </el-date-picker>
    </div>
    <br />
    <el-upload
      ref="upload"
      :limit="1"
      accept=".xlsx, .xls"
      :headers="headers"
      :action="upLoadUrl + '?planDateString=' + this.planDate"
      :disabled="isUploading"
      :on-progress="handleFileUploadProgress"
      :on-success="handleFileSuccess"
      :auto-upload="false"
      drag
    >
      <i class="el-icon-upload"></i>
      <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
      <div class="el-upload__tip text-center" slot="tip">
        <span>仅允许导入xls、xlsx格式文件。</span>
      </div>
    </el-upload>
    <div slot="footer" class="dialog-footer">
      <el-button type="primary" @click="submitFileForm">确 定</el-button>
      <el-button @click="open = false">取 消</el-button>
    </div>
  </el-dialog>
</template>

<script>
import { getToken } from "@/utils/auth";
import moment from "moment";

export default {
  data() {
    return {
      // 是否显示弹出层(用户导入)
      open: false,
      // 弹出层标题(用户导入)
      title: "",
      // 是否禁用上传
      isUploading: false,
      //计划日期
      planDate: new Date(),
      // 设置上传的请求头部
      headers: { Authorization: "Bearer " + getToken() },
      // 上传的地址
      upLoadUrl: "",
    };
  },
  mounted() {
    //默认计划日期为明天
    this.planDate = moment().subtract(-1, "days").format("YYYY-MM-DD");
  },
  methods: {
    /** 导入按钮操作 */
    handleImport(data) {
      this.title = data.title;
      this.upLoadUrl = process.env.VUE_APP_BASE_API + data.upLoadUrl;
      this.open = true;
    },
    // 提交上传文件
    submitFileForm() {
      this.$refs.upload.submit();
    },
    // 文件上传中处理
    handleFileUploadProgress() {
      this.isUploading = true;
    },
    // 文件上传成功处理
    handleFileSuccess(response) {
      this.open = false;
      this.isUploading = false;
      this.$refs.upload.clearFiles();
      this.$alert(
        "<div style='overflow: auto;overflow-x: hidden;max-height: 70vh;padding: 10px 20px 0;'>" +
          response.msg +
          "</div>",
        "导入结果",
        { dangerouslyUseHTMLString: true }
      );
      //上传数据成功后重新请求数据
      this.$emit("getList");
    },
  },
};
</script>

<style>
</style>

导入测试效果

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/6811.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

使用image-map编写校区平面示意图

一、效果图 先上一波效果图&#xff0c;手机录制 二、项目中用到的图片 呐&#xff0c;最近领导让俺帮某学校做一个校区平面示意图的移动端项目&#xff0c;用到的图片就是这样的&#xff0c;不过这是截的图片&#xff0c;放大后会模糊&#xff0c;项目中真正使用的图片有6…

HazelEngine 学习记录 - Profiling

Profiling Intro To Profiling 为了能够清晰的观察整个引擎的性能消耗情况&#xff0c;我们需要一个可视化的工具来进行性能分析&#xff0c;例如 Unity 的内置 Profiler&#xff1a; 其实本质就是将每个函数运行的时间进行可视化&#xff0c;这里我们借用标准库 chrono 来进…

笔记本电脑没有声音如何解决

​笔记本电脑没有声音的现象&#xff0c;也是笔记本电脑的常见运用病况之一,遇到这种情况的话,大家是否知道如何处理呢?下面小编来跟大家说说笔记本电脑没有声音解决方法&#xff0c;希望可以帮助到大家。 工具/原料&#xff1a; 系统版本&#xff1a;windows10系统 品牌型…

python--谷歌恐龙快跑小项目

用300行代码左右实现谷歌休闲的恐龙快跑游戏&#xff01; 主函数&#xff1a; import sys import math import time import random import pygame from pygame.locals import * from Scene import Scene from Obstacle import Plant, Ptera from Dinosaur import Dinosaur #…

嵌入式开发:当用微控制器构建嵌入式GUI时,有哪些注意事项

在嵌入式开发中&#xff0c;借助基于MCU的设计&#xff0c;你可以消除额外的RAM和闪存芯片&#xff0c;并使用板载外设而不是板外逻辑&#xff0c;所有这些都将随着当今功能强大的芯片而变得更加简单。当然&#xff0c;与成熟的微处理器相比&#xff0c;MCU本身也提供了额外的成…

【校内篇】如何安装一台虚拟机

咱们的微机老师要求上微机课用的电脑必须要用 Windows7Windows\ 7Windows 7&#xff0c;但是很多同学的电脑也许并不匹配&#xff0c;造成了诸多不便。 作为班长&#xff0c;我觉得有必要把自己的一些技术共享给大家&#xff0c;方便大家使用。 文章目录一、准备材料&#x1f6…

如何扩大电脑c盘分区,c盘空间不足怎么扩容

当电脑使用一段时间后&#xff0c;C盘会存储一定的数据&#xff0c;包括操作系统以及其他的文件。在实际的运用中&#xff0c;许多应用程序的默认下载路径就是C盘&#xff0c;如果用户没有更改为其他磁盘&#xff0c;会导致C盘的空间越来越小&#xff0c;电脑越来越卡顿。从根源…

【C版本】静态通讯录与动态通讯录的实现,以及各自所存在的缺陷对比。(含所有原码)

目录静态版本通讯录前期思路具体实现1、框架2、初始化通讯录3、增加联系人4、显示已有联系人5、查找联系人6、删除指定联系人7、排序联系人8、修改联系人信息9、清空联系人静态版本通讯录存在的缺陷动态版本通讯录&#xff08;静态版本的部分功能发生改动&#xff09;初始化增加…

优雅的使用Webstack打造个人网址导航

原文链接&#xff1a;优雅的使用Webstack打造个人网址导航 前言 一款基于 WebStackPage 的 Hexo 主题。本人选择的是 hexo-theme-webstack。 效果预览 具体效果请移步 个人网址导航。 步骤 在目标路径&#xff08;我这里选的路径为【D:/studytype/My_Blog】&#xff09;打开…

基于C#制作一个桌面宠物

此文主要基于C#制作一个桌面宠物&#xff0c;可自定义宠物素材图片及打开外部exe的快捷菜单。 实现流程1.1、创建项目1.2、准备素材1.3、控件设置&#xff08;1&#xff09;PictureBox控件&#xff08;2&#xff09;timer控件&#xff08;3&#xff09;contextMenuStrip控件1.4…

学习MySQL必须掌握的13个关键字,你get了吗?

1、三范式 第一范式&#xff1a;每个表的每一列都要保持它的原子性&#xff0c;也就是表的每一列是不可分割的&#xff1b;第二范式&#xff1a;在满足第一范式的基础上&#xff0c;每个表都要保持唯一性&#xff0c;也就是表的非主键字段完全依赖于主键字段&#xff1b;第三范…

【微服务】Nacos2.x服务发现?RPC调用?重试机制?

&#x1f496;Spring家族及微服务系列文章 ✨【微服务】Nacos通知客户端服务变更以及重试机制 【微服务】SpringBoot监听器机制以及在Nacos中的应用 ✨【微服务】Nacos服务端完成微服务注册以及健康检查流程 ✨【微服务】Nacos客户端微服务注册原理流程 ✨【微服务】SpringClou…

Vue 和 React 比,React 好在哪里?

​ 这两个设计理念上就有所区别&#xff0c;类比过来就是&#xff1a;Vue 是自动挡汽车&#xff0c;React 是手动挡汽车。 在 Vue 中&#xff0c;不需要去注意视图和数据的一致性&#xff0c;因为有双向绑定看帮你处理&#xff0c;响应式的。还有一些很方便的 v-if、v-model 之…

软考 - 面向对象开发

⭐设计模式UML详解&#xff1a;https://blog.csdn.net/qq_40274514/article/details/124047443 面向对象基础 面向对象的程序设计 和 面向对象设计区别 面向对象的程序设计涉及到具体的编程语言 面向对象设计只从系统逻辑结构设计解决方案 常见的机制 动态绑定&#xff1a;过…

ESXi5.5远程升级到ESXi6.7 (VMware Hypervisor)

1、ESXi的介质分为两类&#xff0c;以6.7为例&#xff1a; VMware vSphere Hypervisor (ESXi ISO) image (Includes VMware Tools)&#xff08;ISO包&#xff09; VMware vSphere Hypervisor (ESXi) Offline Bundle &#xff08;ZIP包&#xff09; 如果要升级&#xff0c;需要Z…

【畅购商城】详情页详情之商品详情

1.构建详情页 步骤0&#xff1a;确定访问路径 http://localhost:3000/Goods?id1 步骤二&#xff1a;复制 ~/static/goods.html 内容&#xff0c;导入第三方资源&#xff08;css、js&#xff09; head: {title: 列表页面,link: [{rel:stylesheet,href: /style/goods.css},{re…

Sysweld笔记:利用稳态算法加速算法模拟焊接过程的残余应力

作者&#xff1a;贾亚波博士&#xff0c;仿真秀专栏作者 在进行热力耦合的仿真过程中&#xff0c;如果模型足够的长并且热源速度恒定&#xff0c;通常其热学&#xff0c;相变以及热力耦合都会达到稳态的过程&#xff0c;因此如何直接计算稳态问题成为了大家研究的热点问题。 …

【C++笔试强训】第二十四天

&#x1f387;C笔试强训 博客主页&#xff1a;一起去看日落吗分享博主的C刷题日常&#xff0c;大家一起学习博主的能力有限&#xff0c;出现错误希望大家不吝赐教分享给大家一句我很喜欢的话&#xff1a;夜色难免微凉&#xff0c;前方必有曙光 &#x1f31e;。 &#x1f4a6;&a…

【数据结构】栈基本操作的实现(C语言)

&#x1f680; 作者简介&#xff1a;一名在后端领域学习&#xff0c;并渴望能够学有所成的追梦人。 &#x1f40c; 个人主页&#xff1a;蜗牛牛啊 &#x1f525; 系列专栏&#xff1a;&#x1f6f9;初出茅庐C语言、&#x1f6f4;数据结构 &#x1f4d5; 学习格言&#xff1a;博…

SpringBoot项目打包时配置文件区分日常、测试、预发、正式环境

前言&#x1f34a; 在我们开发项目的时候&#xff0c;一般有四套环境&#xff1a;日常、测试、预发、正式。日常环境作为我们开发环境&#xff1b;测试环境给测试同学测试功能&#xff1b;预发环境给正式环境发布时提供准备&#xff1b;正式环境则是稳定的生产环境。 这四套环…