SpringBoot如何优雅的实现参数验证

news2025/1/7 6:07:14

唠嗑部分

在我们设计接口时,参数验证是必不可少的一个环节,严格的参数验证能够保证数据的严谨,那么在SpringBoot项目中,你是如何验证参数的呢?

首先我们来描述一下需求

用户类,有用户名、用户头像、邮件地址、年龄、手机号、出生日期,要求:

1、用户名不能为空且由字母数字下划线组成,不超过16个字符。

2、用户头像不能为空且为一个网络图片。

3、邮箱不能为空且必须为一个合法的邮件地址。

4、年龄不能为空且大于0。

5、手机号不能为空且必须合法。

6、出生日期不能为空且小于当前。

对于以上需求,你的验证方式是否如下呢

@PostMapping("/user/save")
public CommonResult save(@RequestBody UserReq req){
    CommonResult r = CommonResult.success(null);
    String username = req.getUsername();
    String avatar = req.getAvatar();
    Integer age = req.getAge();
    LocalDate bothDate = req.getBothDate();
    String email = req.getEmail();

    String pattern = "[A-Za-z0-9_]+";
    Pattern p = Pattern.compile(pattern);
    Matcher m = p.matcher(username);
    if (!StringUtils.hasLength(username)) {
        return CommonResult.error(400, "用户名岂能为空??");
    } else if (m.matches()) {
        return CommonResult.error(400, "用户名格式有误!");
    } else if (username.length() > 16) {
        return CommonResult.error(400, "用户名长度不大于16位");
    }

    if (!StringUtils.hasLength(avatar)) {
        return CommonResult.error(400, "用户头像岂能为空??");
    } else if (!avatar.startsWith("http://") && !avatar.startsWith("https://")) {
        return CommonResult.error(400, "用户头像格式有误!");
    }
    ...
    // 剩余字段验证类似
    r.setData(req);
    return r;
}

上述代码可以实现各种场景的参数验证,但是仅仅两个字段已经写了20行代码,更不要说后台管理系统中复杂的参数验证了,那有些小伙伴可能会问,既然参数验证这么麻烦,那么能不能接口不验证,将这个麻烦的活交给前端去做呢?

有这个想法的小伙伴趁早打消念头哈,肯定是不行的,前端只能验证从我们系统前端发出的请求,如果有黑客绕过浏览器,使用http工具访问后台服务,并且有一些不好的企图,那么服务就危险了

那么今天我们就来说一说如何优雅的实现参数验证,跟if…else说拜拜吧

言归正传

Spring已经整合了Hibernate Validator,对于SpringBoot来说使用也十分方便,使用 @Validated 注解,实现声明式校验

我们先认识一下相关注解

空和非空检查

  • @NotBlank :只能用于字符串不为 null ,并且字符串 #trim() 以后 length 要大于 0 。
  • @NotEmpty :集合对象的元素不为 0 ,即集合不为空,也可以用于字符串不为 null 。
  • @NotNull :不能为 null 。
  • @Null :必须为 null 。

数值检查

  • @DecimalMax(value) :被注释的元素必须是一个数字,其值必须小于等于指定的最大值。
  • @DecimalMin(value) :被注释的元素必须是一个数字,其值必须大于等于指定的最小值。
  • @Digits(integer, fraction) :被注释的元素必须是一个数字,其值必须在可接受的范围内。
  • @Positive :判断正数。
  • @PositiveOrZero :判断正数或 0 。
  • @Max(value) :该字段的值只能小于或等于该值。
  • @Min(value) :该字段的值只能大于或等于该值。
  • @Negative :判断负数。
  • @NegativeOrZero :判断负数或 0 。

Boolean 值检查

  • @AssertFalse :被注释的元素必须为 true 。
  • @AssertTrue :被注释的元素必须为 false 。

长度检查

  • @Size(max, min) :检查该字段的 size 是否在 min 和 max 之间,可以是字符串、数组、集合、Map 等。

日期检查

  • @Future :被注释的元素必须是一个将来的日期。
  • @FutureOrPresent :判断日期是否是将来或现在日期。
  • @Past :检查该字段的日期是在过去。
  • @PastOrPresent :判断日期是否是过去或现在日期。

其它检查

  • @Email :被注释的元素必须是电子邮箱地址。
  • @Pattern(value) :被注释的元素必须符合指定的正则表达式。

Hibernate Validator 附加的约束注解

org.hibernate.validator.constraints 包下,定义了一系列的约束( constraint )注解。如下:

  • @Range(min=, max=) :被注释的元素必须在合适的范围内。
  • @Length(min=, max=) :被注释的字符串的大小必须在指定的范围内。
  • @URL(protocol=,host=,port=,regexp=,flags=) :被注释的字符串必须是一个有效的 URL 。
  • @SafeHtml :判断提交的 HTML 是否安全。例如说,不能包含 javascript 脚本等等。

下面就让我们来体验一下

1、首先导入依赖

小白记得在2.1.6版本的时候,这个依赖是不用导入的,但是具体在哪个版本,官方将这个依赖分离了出来,2.4.2需要手动导入

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>

2、DTO添加参数验证注解

@Data
public class UserReq {

    @NotBlank(message = "用户名岂能为空")
    @Pattern(regexp = "[A-Za-z0-9_]+", message = "用户名格式有误")
    @Size(max = 16, message = "用户名不能大于16位")
    private String username;

    @NotBlank(message = "用户头像岂能为空")
    @Pattern(regexp = "[a-zA-z]+:\\/\\/[^\\s]*", message = "用户头像格式有误")
    private String avatar;

    @NotBlank(message = "邮箱岂能为空")
    @Email(message = "邮箱格式有误")
    private String email;

    @NotNull(message = "年龄岂能为空")
    @Min(value = 0, message = "年龄必须大于0")
    private Integer age;

    @NotBlank(message = "手机号岂能为空")
    @Pattern(regexp = "0?(13|14|15|17|18|19)[0-9]{9}", message = "手机号格式有误")
    private String phone;

    @NotNull(message = "出生日期岂能为空")
    @Past(message = "出生日期不可大于当前")
    @JsonFormat(pattern = "yyyy-MM-dd")
    private LocalDate bothDate;
}

3、接口参数添加@Validated注解

@PostMapping("/user/save")
public CommonResult save(@RequestBody @Validated UserReq req){
    CommonResult r = CommonResult.success(null);
    r.setData(req);
    return r;
}

4、添加全局异常处理器,处理参数验证异常

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(value = ConstraintViolationException.class)
    public CommonResult constraintViolationExceptionHandler(HttpServletRequest req, ConstraintViolationException ex) {
        // 拼接错误
        StringBuilder detailMessage = new StringBuilder();
        for (ConstraintViolation<?> constraintViolation : ex.getConstraintViolations()) {
            if (detailMessage.length() > 0) {
                detailMessage.append(";");
            }
            detailMessage.append(constraintViolation.getMessage());
        }
        return CommonResult.error(400, detailMessage.toString());
    }

    @ExceptionHandler(value = BindException.class)
    public CommonResult bindExceptionHandler(HttpServletRequest req, BindException ex) {
        // 拼接错误
        StringBuilder detailMessage = new StringBuilder();
        for (ObjectError objectError : ex.getAllErrors()) {
            if (detailMessage.length() > 0) {
                detailMessage.append(";");
            }
            detailMessage.append(objectError.getDefaultMessage());
        }
        return CommonResult.error(400, detailMessage.toString());
    }

    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    public CommonResult MethodArgumentNotValidExceptionHandler(HttpServletRequest req, MethodArgumentNotValidException ex) {
        // 拼接错误
        StringBuilder detailMessage = new StringBuilder();
        for (ObjectError objectError : ex.getBindingResult().getAllErrors()) {
            if (detailMessage.length() > 0) {
                detailMessage.append(";");
            }
            detailMessage.append(objectError.getDefaultMessage());
        }
        return CommonResult.error(400, detailMessage.toString());
    }


    /**
     * 处理其它 Exception 异常
     *
     * @param req
     * @param e
     * @return
     */
    @ExceptionHandler(value = Exception.class)
    public CommonResult exceptionHandler(HttpServletRequest req, Exception e) {
        return CommonResult.error(500, e.getMessage());
    }
}

5、验证错误参数

image-20230522112409593

6、验证正确参数

image-20230522112445841

结语

1、大多数场景下,我们使用 @Validated 注解即可。如果有嵌套校验的场景,使用 @Valid 注解添加到成员属性上。

2、保证接口的健壮性,参数验证是必不可少的环节,不可省略,而且应该极为严格,绝不能让恶意用户有可乘之机。

3、制作不易,一键三连再走吧,您的支持永远是我最大的动力!

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

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

相关文章

基于html+css的图展示88

准备项目 项目开发工具 Visual Studio Code 1.44.2 版本: 1.44.2 提交: ff915844119ce9485abfe8aa9076ec76b5300ddd 日期: 2020-04-16T16:36:23.138Z Electron: 7.1.11 Chrome: 78.0.3904.130 Node.js: 12.8.1 V8: 7.8.279.23-electron.0 OS: Windows_NT x64 10.0.19044 项目…

2000万的行数在2023年仍然是 MySQL 表的有效软限制吗?

谣言 互联网上有传言说我们应该避免在单个 MySQL 表中有超过 2000 万行。否则&#xff0c;表的性能会下降&#xff0c;当它超过软限制时&#xff0c;你会发现 SQL 查询比平时慢得多。这些判断是在多年前使用HDD硬盘存储时做出的。我想知道在2023年对于基于SSD的MySQL数据库来说…

【大数据学习篇10】Spark项目实战~网站转化率统计

学习目标/Target 掌握网站转化率统计实现思路 了解如何生成用户浏览网页数据 掌握如何创建Spark连接并读取数据集 掌握利用Spark SQL统计每个页面访问次数 掌握利用Spark SQL获取每个用户浏览网页的顺序 掌握利用Spark SQL合并同一用户浏览的网页 掌握利用Spark SQL统计每…

安卓基础巩固(三)多线程、数据存储、文件IO、SQLite

文章目录 多线程Handler相关概念UI线程/主线程MessageMessage QueueLooperHandler 使用步骤Handler.sendMessage&#xff08;&#xff09;Handler.post&#xff08;&#xff09; Handler 机制工作原理Handler内存泄露前置知识案例分析解决方案一&#xff1a;静态内部类弱引用解…

数据结构学习记录——图应用实例-六度空间(题目描述、算法思路、伪代码及解读、图解)

目录 题目描述 算法思路 伪代码 总体算法 BFS算法 伪代码解读 BFS算法 图解 题目描述 六度空间理论的核心观点是&#xff0c;人类社交网络中的任何两个人之间&#xff0c;平均只需要通过不超过六个中间人&#xff08;也就是六个社交关系&#xff09;就可以建立联系。换…

多台plc之间如何快速实现以太网无线连接?

常规来说&#xff0c;多台plc要实现以太网无线连接&#xff0c;首先要先确定以太网线必须正确连接&#xff0c;并建立物理连接。然后需要在PLC端设置好IP地址&#xff0c;以使不同PLC以相同协议可以实现通信交流。最后是建立PLC端数据采集及交换系统&#xff0c;要求在PLC端设置…

《封号码罗》关于js逆向猿人学第二题cookies里面m值的获取[纯扣算法](二十六)

这一题有点儿误打误撞的感觉。 本题使用了抓包工具Fiddler&#xff0c;m值在cookie里面&#xff0c;而且这个cookie是本地生成的 抓包发现有两次请求&#xff0c;第一次返回了一堆JS&#xff0c;而且cookie里面没有m值&#xff0c;第二次请求就带上了m值&#xff0c;所以m应该…

信号完整性分析基础知识之传输线和反射(七):带负载传输线、感性不连续引起的反射

带负载传输线 如果在传输线上有一个小的容性负载&#xff0c;信号会出现失真&#xff0c;上升时间也会降低。每个分立电容都会降低信号在其附近看到的阻抗。如果传输线上分布有多个容性负载&#xff08;例如一个总线上每隔1.2inch有一个2pF的连接器残桩&#xff0c;或者一个内…

单模光纤一维模场分布的MATLAB仿真

根据已知的单模光纤电场z分量分布&#xff0c;可以用MATLAB展示一维的模场分布 具体来说&#xff0c;通过数值计算解出给定光纤&#xff08;n_1&#xff0c;n_2&#xff0c;a&#xff09;参数时对应的V参量 通过特征方程解出V对应的W和U 通过这三个参数带入到光场的表达式中…

07. 算法之一致性哈希算法介绍

前言 哈希算法在程序开发中的很多地方都能看到他的身影&#xff0c;但是哈希有他的局限性&#xff0c;比如如果两个key哈希到同一个位置的时候&#xff0c;此时就不好处理。本节我们介绍一下常规处理方式。 1. 什么是哈希算法 哈希算法将任意长度的二进制值映射为较短的固定…

界面控件DevExtreme使用指南 - 如何自定义上下文菜单和工具栏

DevExtreme File Manager&#xff08;文件管理器&#xff09;小部件现在支持自定义内置的工具栏和上下文菜单&#xff0c;用户可以使用标准和定义的命令项填充项目集合&#xff0c;并配置设置来更改其外观和操作。 DevExtreme拥有高性能的HTML5 / JavaScript小部件集合&#x…

基于Keras-YOLO实现目标检测

Keras-YOLO 3项目使用Python语言实现了YOLO v3网络模型&#xff0c;并且可以导入Darknet网络预先训练好的权重文件信息直接使用网络进行目标识别。 1. 下载Keras-YOLO 3项目 执行如下命令下载Keras-YOLO 3项目代码&#xff1a; git clone https://github.com/qqwweee/keras-…

直播和短视频美颜sdk的开发流程、代码分析

目前&#xff0c;美颜技术是提高视频质量的重要手段之一&#xff0c;特别是短视频和直播两个行业。本文将介绍其开发流程和代码分析。 一、美颜SDK的开发流程 1.需求分析 首先我们需要明确的一点就是“需求”&#xff0c;例如&#xff1a;美颜效果、美颜程度、性能要求等。同…

解决找不到微信支付V3版本公钥问题

参考微信文档链接为签名验证-接口规则 | 微信支付商户平台文档中心 写的内容特别不明显&#xff0c;往下面看会找到 下载openssl工具使用命令从私钥证书中导出即可。

地图在线编辑平台,无基础轻松实现私域地图

位构云平台让用户轻松构建诸如空间信息管理、建筑信息管理及三维空间数据可视化、导航等类型应用的多平台、综合型地图引擎&#xff0c;基于OpenGLES/WebGL三维可视化技术体系的自主研发图形引擎&#xff0c;可以让开发者轻松构建运行在 Web、Android、iOS 等多平台的应用程序。…

安捷伦DSO80404B(Agilent)dso80404b租售回收 数字示波器

DSO80404B 是 Agilent 的 4 GHz、4 通道数字示波器。测量电子电路或组件中随时间变化的电压或电流信号&#xff0c;以显示振幅、频率和上升时间等。应用包括故障排除、生产测试和设计。 附加功能&#xff1a; 4 GHz 带宽&#xff0c;可升级至 13 GHz 4个模拟通道 高达 40 G…

day10 前端技术-HTMLCSS

HTML 含义:超文本标记语言,静态网页,用于在浏览器显示数据 双标签:<> </>,开始标签和结束标签同时出现 单标签: 属性名:属性后面的值都加双引号 常用的HTML标签 :文档的根标签 :HTML页面的头部标签 “”:页面标题 “”:页面主体部分 “ “ “ 到 ”:标题…

『树莓派云台机器人』02. 电脑连接树莓派 配置开发环境

目录 1. 下载ssh交互工具 Xshell 与XFTP&#xff08;有过相关使用经历的朋友可以跳过这一节内容&#xff09;2. 下载VNC远程控制工具软件3. 连接过程4. Xshell 命令工具5. XFTP 文件传送工具6. 关于联网总结 欢迎关注 『树莓派云台机器人』 博客&#xff0c;持续更新中 欢迎关注…

PoseiSwap IDO在Bounce上启动在即,如何参与?

目前&#xff0c;Nautilus Chain 生态基本完成测试&#xff0c;并即将在不久上线主网。PoseiSwap 作为 Nautilus Chain 上的首个 DEX&#xff0c;也即将面向市场并上线正式版本。我们看到&#xff0c; PoseiSwap 也正式发布了新的市场进程&#xff0c;基于其治理代币 POSE 的 I…

转辙机介绍

简介 转辙机是指用以可靠地转换道岔位置&#xff0c;改变道岔开通方向&#xff0c;锁闭道岔尖轨&#xff0c;反映道岔位置的重要的信号基础设备&#xff0c;它可以很好地保证行车安全&#xff0c;提高运输效率&#xff0c;改善行车人员的劳动强度。 分类 01、转辙机按动作时…