当前位置: 首页 > news >正文

如何在Spring Boot中优雅地进行参数校验

1. 前言

在平时的开发工作中,我们通常需要对接口进行参数格式验证。当参数个数较少(个数小于3)时,可以使用if ... else ...手动进行参数验证。当参数个数大于3个时,使用if ... else ...进行参数验证就会让代码显得臃肿,这个时候推荐使用注解来进行参数验证。

在Java中,注解(Annotation)是一种代码标记,通常用于提供元数据,这些元数据可以被编译器或运行时环境使用。这些注解通常用于框架和库中,以实现更加灵活和可配置的代码。

2. 常用注解描述

  1. @NotNull

    • 描述:标记一个值不能为null。
    • 示例:
    public class User {  @NotNull  private String name;  // ...  
    }
    
  2. @NotEmpty

    • 描述:标记一个集合(如List、Set等)不能为空。
    • 示例:
    public class User {  @NotEmpty  private List<String> interests;  // ...  
    }
    
  3. @NotBlank

    • 描述:标记一个字符串不能为空白(即null、空字符串或只包含空格)。
    • 示例:
    public class User {  @NotBlank  private String username;  // ...  
    }
    
  4. @Size

    • 描述:标记一个字符串或集合的大小必须在指定的范围内。
    • 示例:
    public class User {  @Size(min = 2, max = 50)  private String username;  // ...  
    }
    
  5. @Min@Max

    • 描述:标记一个数值必须在指定的最小值和最大值之间。
    • 示例:
    public class User {  @Min(18)@Max(60)  private int age;  // ...  
    }
    
  6. @DecimalMin@DecimalMax

    • 描述:标记一个浮点数或双精度数必须在指定的最小值和最大值之间。
    • 示例:
    public class User {  @DecimalMin("0.01") @DecimalMax("100.00")  private double discount;  // ...  
    }
    
  7. @Digits

    • 描述:标记一个整数或浮点数必须在指定的精度和总数值范围内。
    • 示例:
    public class User {  @Digits(integer = 3, fraction = 2) // 总长度为5,3位整数,2位小数。例如:"123.45" 是合法的。  private BigDecimal amount;  // ...  
    }
    
  8. @Pattern

    • 描述:标记一个字符串必须匹配指定的正则表达式。通常用于验证输入格式。例如电子邮件地址、电话号码格式等。@Pattern注解在javax.validation.constraints包中。@Pattern(regexp = “^\w{5,}$”)表示长度在5-20之间,由字母、数字、下划线组成的字符串。@Pattern注解用于类字段上,例如用户密码字段。
    • 示例:
    public class User { @Pattern(regexp = "^[a-zA-Z0-9]*$") private String password; //... 
    } 
    
  9. @Email

    • 描述:标记一个字符串必须是一个有效的电子邮件地址。
    • 示例:
    @Email
    private String emailAddress;
    
  10. @AssertTrue@AssertFalse

    • 描述:标记一个布尔值必须为true或false。
    • 示例:
    @AssertTrue
    private boolean isValid;@AssertFalse
    private boolean isNotValid;
    
  11. @Future

    • 描述:标记一个日期必须是在未来某个时间点之后。
    • 示例:
    @Future
    private Date expiryDate;
    
  12. @Past

    • 描述:标记一个日期必须是在过去某个时间点之前。
    • 示例:
    @Past
    private Date purchaseDate;
    

这些注解通常与验证框架(如Hibernate Validator)一起使用,以在运行时验证对象的属性。

3. 注解使用

  1. 在项目的pom.xml文件中添加如下依赖:
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId></dependency>
  1. 在实体类中使用上述注解,代码如下:

    package com.yyqq.demo.entity;import lombok.Data;
    import javax.validation.constraints.*;@Data
    public class User {@NotBlank(message = "用户姓名不能为空")private String name;@NotBlank(message = "密码不能为空")@Size(min = 6, message = "密码长度不能少于6位")private String password;@Min(value = 0, message = "年龄不能小于0岁")@Max(value = 150, message = "年龄不应超过150岁")private Integer age;@Pattern(regexp = "^((13[0-9])|(15[^4])|(18[0-9])|(17[0-9])|(147))\d{8}$", message = "手机号格式不正确")private String phone;
    }
    
  2. 控制器类使用验证,代码如下:

    import com.yyqq.demo.util.Result;
    import com.yyqq.demo.entity.User;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;import javax.validation.Valid;@RestController
    @RequestMapping("/user")
    public class UserController {@PostMapping("/add")public Result add(@Valid @RequestBody User user) {return Result.success(user);}
    }
    
  3. Result是封装结果的一个类,用于返回统一的结果,代码如下:

    package com.yyqq.demo.util;import lombok.Data;
    import java.io.Serializable;@Data
    public class Result<T> implements Serializable {private int code;private boolean success;private T data;private String msg;private Result(int code, T data, String msg) {this.code = code;this.data = data;this.msg = msg;this.success = code == 200;}public static <T> Result<T> sucess(T data) {return new Result<>(200, data, null);}public static <T> Result<T> fail(String msg) {return new Result<>(500, null, msg);}
    }
    
  4. 定义全局异常处理类,我们在全局异常处理类中使用ExceptionHandler捕获BindException异常,获取参数验证异常信息,最后返回统一的异常结果格式,代码如下:

    package com.yyqq.demo.util;import com.yyqq.demo.util.Result;
    import org.springframework.validation.BindException;
    import org.springframework.validation.BindingResult;
    import org.springframework.web.bind.annotation.ExceptionHandler;
    import org.springframework.web.bind.annotation.RestControllerAdvice;@RestControllerAdvice
    public class GlobalExceptionHandler {@ExceptionHandler(BindException.class)public Result handleError(BindException e) {BindingResult bindingResult = e.getBindingResult();return Result.fail(bindingResult.getFieldError().getDefaultMessage());}
    }
    

4. 使用分组验证

  1. 用于插入记录时的分组验证,代码如下:
package com.yyqq.demo.interceptor;
import javax.validation.groups.Default;public interface Insert extends Default {
}
  1. 用于更新记录时的分组验证,代码如下:
package com.yyqq.demo.interceptor;
import javax.validation.groups.Default;public interface Update extends Default {
}
  1. 在实体类中进行分组标记,代码如下:
package com.yyqq.demo.entity;import lombok.Data;
import javax.validation.constraints.*;@Data
public class User {@NotBlank(groups = {Insert.class, Update.class})@NotBlank(message = "用户姓名不能为空")private String name;@NotBlank(message = "密码不能为空")@Size(min = 6, message = "密码长度不能少于6位")private String password;@Min(value = 0, message = "年龄不能小于0岁")@Max(value = 150, message = "年龄不应超过150岁")private Integer age;@NotBlank(groups = {Insert.class, Update.class})@Pattern(regexp = "^((13[0-9])|(15[^4])|(18[0-9])|(17[0-9])|(147))\\d{8}$", message = "手机号格式不正确")private String phone;
}
  1. 控制器类使用分组验证
package com.yyqq.demo.controller;import com.yyqq.demo.util.Result;
import com.yyqq.demo.entity.User;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import javax.validation.Valid;@RestController
@RequestMapping("/user")
public class UserController {@PostMapping("/add")public Result add(@Validated(Insert.class) @RequestBody User user) {return Result.success(user);}@PostMapping("/update")public Result update(@Validated(Update.class) @RequestBody User user) {return Result.success(user);}
}

5. 自定义验证注解

除了框架自带的注解,平时的工作中可能需要我们自定义验证注解处理特定的业务需求。

  1. 定义注解
package com.yyqq.demo.validate;import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;@Documented
@Retention(RUNTIME)
@Constraint(validatedBy = {PhoneValidator.class})
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
public @interface Phone {String message() default "手机号格式错误";Class<?>[] groups() default {};Class<? extends Payload>[] payload() default {};
}
  1. 定义验证器类
package com.yyqq.demo.validate;import javax.validation.ConstraintValidatorContext;
import javax.validation.ConstraintValidator;
import java.util.regex.Pattern;public class PhoneValidator implements ConstraintValidator<Phone, String> {private static final String REGEX = "^((13[0-9])|(15[^4])|(18[0-9])|(17[0-9])|(147))\\d{8}$";@Overridepublic boolean isValid(String s, ConstraintValidatorContext context) {boolean result = false;try {result = Pattern.matches(REGEX, s);} catch (Exception e) {System.out.println("验证手机号格式时发生异常,异常信息:" + e);}return result;}
}
  1. 实体类使用注解
package com.yyqq.demo.validate;public class User {//其他属性...//  @Pattern(regexp = "^((13[0-9])|(15[^4])|(18[0-9])|(17[0-9])|(147))\\d{8}$", message = "手机号格式不正确")@Phoneprivate String phone;
}

6. @Valid与@Validated的区别

用于参数校验的注解通常有两个:@Valid@Validated。它们的区别有如下几点:

区别@Valid@Validated
来源@Valid是Java标准注解@Validated是Spring框架定义的注解。
是否支持分组验证不支持支持
使用位置构造函数、方法、方法参数、成员属性类、方法、方法参数,不能用于成员属性
是否支持嵌套校验支持不支持

相关文章:

如何在Spring Boot中优雅地进行参数校验

1. 前言 在平时的开发工作中&#xff0c;我们通常需要对接口进行参数格式验证。当参数个数较少&#xff08;个数小于3&#xff09;时&#xff0c;可以使用if ... else ...手动进行参数验证。当参数个数大于3个时&#xff0c;使用if ... else ...进行参数验证就会让代码显得臃肿…...

图还能有数据库?一文带你了解图数据库是个什么东西!

图数据库 基础 简介 %% 图数据库是图数据库管理系统的简称&#xff0c;是近年来新兴的一种NoSQL数据库使用图形化的模型进行查询的数据库&#xff0c;通过节点、边和属性等方式来表示和存储数据&#xff0c;支持增删改查::CRUD::等操作。图数据库一般用于OLTP系统中&#xf…...

力扣思维题——寻找重复数

题目链接&#xff1a;https://leetcode.cn/problems/find-the-duplicate-number/description/?envTypestudy-plan-v2&envIdtop-100-liked 这题的思维难度较大。一种是利用双指针法进行计算环的起点&#xff0c;这种方法在面试里很难说清楚&#xff0c;也很难想到。大致做…...

基于Kubernetes的jenkins上线

1、基于helm 部署jenkins 要求&#xff1a;当前集群配置了storageClass&#xff0c;并已指定默认的storageClass&#xff0c;一般情况下&#xff0c;创建的storageClass即为默认类 指定默认storageClass的方式 # 如果是新创建默认类&#xff1a; apiVersion: storage.k8s.io/v1…...

每日一题——轮转数组

1. 题目描述 给定一个整数数组nums&#xff0c;将数组中的元素向右轮转k个位置&#xff0c;其中k是非负数。 示例1: 输入&#xff1a;nums [1,2,3,4,5,6,7]&#xff0c;k 3 输出&#xff1a;[5,6,7,1,2,3,4] 解释&#xff1a; 向右轮转 1步&#xff1a;[7,1,2,3,4,5,6] 向右…...

Unity手机移动设备重力感应

Unity手机移动设备重力感应 一、引入二、介绍三、测试成果X Y轴Z轴横屏的手机&#xff0c;如下图竖屏的手机&#xff0c;如下图 一、引入 大家对重力感应应该都不陌生&#xff0c;之前玩过的王者荣耀的资源更新界面就是使用了重力感应的概念&#xff0c;根据手机的晃动来给实体…...

nodejs微信小程序+python+PHP基于推荐算法的电影推荐系统-计算机毕业设计推荐django

目 录 摘 要 I ABSTRACT II 目 录 II 第1章 绪论 1 1.1背景及意义 1 1.2 国内外研究概况 1 1.3 研究的内容 1 第2章 相关技术 3 2.1 nodejs简介 4 2.2 express框架介绍 6 2.4 MySQL数据库 4 第3章 系统分析 5 3.1 需求分析 5 3.2 系统可行性分析 5 3.2.1技术可行性&#xff1a;…...

Linux 配置 swap 区

Linux 配置 swap 区 很多时候我们需要配置 swap 主要的原因是物理内存太贵了&#xff0c; 服务器也是一样&#xff0c; 当内存不够用时&#xff0c; 系统会卡死&#xff0c; 因此我们宁愿牺牲一点性能也要让系统正常运行。 当然&#xff0c; 在系统物理内存足够的条件下&#x…...

AG16KDDF256 User Manual

AGM AG16KDDF256 是由 AGM FPGA AG16K 与 DDR-SDRAM 叠封集成的芯片&#xff0c;具有 AG16K FPGA的可编程功能&#xff0c;提供更多可编程 IO&#xff0c;同时内部连接大容量 DDR-SDRAM。  FPGA 外部管脚 FBGA256 封装&#xff0c;管脚说明请见下表 Table-1&#xff1a; Tab…...

w15初识php基础

一、计算100之内的偶数之和 实现思路 所有的偶数除2都为0 代码实现 <?php # 记录100以内的偶数和 $number1; $num0; while($number<100){if($number%20){ $num$number;}$number1; } echo $num; ?>输出的结果 二、计算100之内的奇数之和 实现思路 所有的奇数除…...

powerbuilder Primary! Delete! Filter! 三个缓冲区的作用

Primary! 主缓存区&#xff0c;放正在使用的数据。 Delete! 删除缓存区&#xff0c;放将要删除但还没有提交到数据库的数据。 Filter! 筛选缓存区&#xff0c;放不符合筛选条件的数据。 最后在update的时候根据你的update设置生成相应的SQL语句。行的状态和所在的缓存区决定生…...

Confluent 与阿里云将携手拓展亚太市场,提供消息流平台服务

10 月 31 日&#xff0c;杭州云栖大会上&#xff0c;阿里云云原生应用平台负责人丁宇宣布&#xff0c;Confluent 成为阿里云技术合作伙伴&#xff0c;合作全新升级&#xff0c;一起拓展和服务亚太市场。 本次合作伙伴签约&#xff0c;阿里云与消息流开创领导者 Confluent 将进一…...

【一起学Rust | 框架篇 | Tauri2.0框架】Tauri2.0环境搭建与项目创建

文章目录 前言一、搭建 Tauri 2.0 开发环境二、创建 Tauri 2.0 项目1.创建项目2.安装依赖4. 编译运行 三、设置开发环境四、项目结构 前言 Tauri在Rust圈内成名已久&#xff0c;凭借Rust的可靠性&#xff0c;使用系统原生的Webview构建更小的App 以及开发人员可以灵活的使用各…...

算法基础之01背包问题

01背包问题 核心思想&#xff1a; 二维数组普通写法: #include<iostream>#include<cstring>#include<algorithm>using namespace std;const int N 1010;int f[N][N]; //存 i个物品 容量不超过j 的总价值int v[N],w[N];int n,m;int main(){cin>>n>…...

Git的总体认知与具体实现

GIt概念 是一种分布式控制管理器 tips:敏捷开发 -> 先上线&#xff0c;后续开发再继续开发 集中式和分布式 集中式的版本控制系统每次在写代码时都需要从服务器中拉取一份下来&#xff0c;并且如果服务器丢失了&#xff0c;那么所有的就都丢失了&#xff0c;你本机客户端仅…...

Hadoop入门学习笔记——三、使用HDFS文件系统

视频课程地址&#xff1a;https://www.bilibili.com/video/BV1WY4y197g7 课程资料链接&#xff1a;https://pan.baidu.com/s/15KpnWeKpvExpKmOC8xjmtQ?pwd5ay8 Hadoop入门学习笔记&#xff08;汇总&#xff09; 目录 三、使用HDFS文件系统3.1. 使用命令操作HDFS文件系统3.1.…...

JavaWeb—html, css, javascript, dom,xml, tomcatservlet

文章目录 快捷键HTML**常用特殊字符替代:****标题****超链接标签****无序列表、有序列表****无序列表**:ul/li 基本语法**有序列表ol/li:****图像标签(img)**** 表格(table)标签****表格标签-跨行跨列表格****form(表单)标签介绍****表单form提交注意事项**div 标签p 标签sp…...

LangChain 31 模块复用Prompt templates 提示词模板

LangChain系列文章 LangChain 实现给动物取名字&#xff0c;LangChain 2模块化prompt template并用streamlit生成网站 实现给动物取名字LangChain 3使用Agent访问Wikipedia和llm-math计算狗的平均年龄LangChain 4用向量数据库Faiss存储&#xff0c;读取YouTube的视频文本搜索I…...

深入理解 Git 分支管理:提升团队协作与开发效率

目录 前言1 什么是分支2 分支的好处2.1 并行开发的支持2.2 独立性与隔离性2.3 灵活的版本控制2.4 提高安全性和代码质量2.5 项目历史的清晰记录 3 Git 分支操作命令3.1 git branch -v3.2 git branch 分支名称3.3 git checkout 分支名称3.4 git merge 分支名称3.5 git rebase 分…...

WPF StackPanel

StackPanel是一个控件容器&#xff0c;它按照一个方向&#xff08;水平或垂直&#xff09;堆叠子元素&#xff0c;使得它们沿一个轴线对齐。你可以在StackPanel中放置其他控件&#xff0c;如按钮、标签、文本框、图片等等。这些控件的排列方式由StackPanel按照指定的方向自动确…...

应用升级/灾备测试时使用guarantee 闪回点迅速回退

1.场景 应用要升级,当升级失败时,数据库回退到升级前. 要测试系统,测试完成后,数据库要回退到测试前。 相对于RMAN恢复需要很长时间&#xff0c; 数据库闪回只需要几分钟。 2.技术实现 数据库设置 2个db_recovery参数 创建guarantee闪回点&#xff0c;不需要开启数据库闪回。…...

如何在看板中体现优先级变化

在看板中有效体现优先级变化的关键措施包括&#xff1a;采用颜色或标签标识优先级、设置任务排序规则、使用独立的优先级列或泳道、结合自动化规则同步优先级变化、建立定期的优先级审查流程。其中&#xff0c;设置任务排序规则尤其重要&#xff0c;因为它让看板视觉上直观地体…...

深入浅出:JavaScript 中的 `window.crypto.getRandomValues()` 方法

深入浅出&#xff1a;JavaScript 中的 window.crypto.getRandomValues() 方法 在现代 Web 开发中&#xff0c;随机数的生成看似简单&#xff0c;却隐藏着许多玄机。无论是生成密码、加密密钥&#xff0c;还是创建安全令牌&#xff0c;随机数的质量直接关系到系统的安全性。Jav…...

ESP32读取DHT11温湿度数据

芯片&#xff1a;ESP32 环境&#xff1a;Arduino 一、安装DHT11传感器库 红框的库&#xff0c;别安装错了 二、代码 注意&#xff0c;DATA口要连接在D15上 #include "DHT.h" // 包含DHT库#define DHTPIN 15 // 定义DHT11数据引脚连接到ESP32的GPIO15 #define D…...

测试markdown--肇兴

day1&#xff1a; 1、去程&#xff1a;7:04 --11:32高铁 高铁右转上售票大厅2楼&#xff0c;穿过候车厅下一楼&#xff0c;上大巴车 &#xffe5;10/人 **2、到达&#xff1a;**12点多到达寨子&#xff0c;买门票&#xff0c;美团/抖音&#xff1a;&#xffe5;78人 3、中饭&a…...

c++ 面试题(1)-----深度优先搜索(DFS)实现

操作系统&#xff1a;ubuntu22.04 IDE:Visual Studio Code 编程语言&#xff1a;C11 题目描述 地上有一个 m 行 n 列的方格&#xff0c;从坐标 [0,0] 起始。一个机器人可以从某一格移动到上下左右四个格子&#xff0c;但不能进入行坐标和列坐标的数位之和大于 k 的格子。 例…...

《通信之道——从微积分到 5G》读书总结

第1章 绪 论 1.1 这是一本什么样的书 通信技术&#xff0c;说到底就是数学。 那些最基础、最本质的部分。 1.2 什么是通信 通信 发送方 接收方 承载信息的信号 解调出其中承载的信息 信息在发送方那里被加工成信号&#xff08;调制&#xff09; 把信息从信号中抽取出来&am…...

DIY|Mac 搭建 ESP-IDF 开发环境及编译小智 AI

前一阵子在百度 AI 开发者大会上&#xff0c;看到基于小智 AI DIY 玩具的演示&#xff0c;感觉有点意思&#xff0c;想着自己也来试试。 如果只是想烧录现成的固件&#xff0c;乐鑫官方除了提供了 Windows 版本的 Flash 下载工具 之外&#xff0c;还提供了基于网页版的 ESP LA…...

令牌桶 滑动窗口->限流 分布式信号量->限并发的原理 lua脚本分析介绍

文章目录 前言限流限制并发的实际理解限流令牌桶代码实现结果分析令牌桶lua的模拟实现原理总结&#xff1a; 滑动窗口代码实现结果分析lua脚本原理解析 限并发分布式信号量代码实现结果分析lua脚本实现原理 双注解去实现限流 并发结果分析&#xff1a; 实际业务去理解体会统一注…...

3-11单元格区域边界定位(End属性)学习笔记

返回一个Range 对象&#xff0c;只读。该对象代表包含源区域的区域上端下端左端右端的最后一个单元格。等同于按键 End 向上键(End(xlUp))、End向下键(End(xlDown))、End向左键(End(xlToLeft)End向右键(End(xlToRight)) 注意&#xff1a;它移动的位置必须是相连的有内容的单元格…...