1. 前言
在平时的开发工作中,我们通常需要对接口进行参数格式验证。当参数个数较少(个数小于3)时,可以使用if ... else ...
手动进行参数验证。当参数个数大于3个时,使用if ... else ...
进行参数验证就会让代码显得臃肿,这个时候推荐使用注解来进行参数验证。
在Java中,注解(Annotation)是一种代码标记,通常用于提供元数据,这些元数据可以被编译器或运行时环境使用。这些注解通常用于框架和库中,以实现更加灵活和可配置的代码。
2. 常用注解描述
@NotNull
- 描述:标记一个值不能为null。
- 示例:
public class User { @NotNull private String name; // ... }
@NotEmpty
- 描述:标记一个集合(如List、Set等)不能为空。
- 示例:
public class User { @NotEmpty private List<String> interests; // ... }
@NotBlank
- 描述:标记一个字符串不能为空白(即null、空字符串或只包含空格)。
- 示例:
public class User { @NotBlank private String username; // ... }
@Size
- 描述:标记一个字符串或集合的大小必须在指定的范围内。
- 示例:
public class User { @Size(min = 2, max = 50) private String username; // ... }
@Min 和 @Max
- 描述:标记一个数值必须在指定的最小值和最大值之间。
- 示例:
public class User { @Min(18) @Max(60) private int age; // ... }
@DecimalMin 和 @DecimalMax
- 描述:标记一个浮点数或双精度数必须在指定的最小值和最大值之间。
- 示例:
public class User { @DecimalMin("0.01") @DecimalMax("100.00") private double discount; // ... }
@Digits
- 描述:标记一个整数或浮点数必须在指定的精度和总数值范围内。
- 示例:
public class User { @Digits(integer = 3, fraction = 2) // 总长度为5,3位整数,2位小数。例如:"123.45" 是合法的。 private BigDecimal amount; // ... }
@Pattern
- 描述:标记一个字符串必须匹配指定的正则表达式。通常用于验证输入格式。例如电子邮件地址、电话号码格式等。@Pattern注解在javax.validation.constraints包中。@Pattern(regexp = “^\w{5,}$”)表示长度在5-20之间,由字母、数字、下划线组成的字符串。@Pattern注解用于类字段上,例如用户密码字段。
- 示例:
public class User { @Pattern(regexp = "^[a-zA-Z0-9]*$") private String password; //... }
@Email
- 描述:标记一个字符串必须是一个有效的电子邮件地址。
- 示例:
@Email private String emailAddress;
@AssertTrue 和 @AssertFalse
- 描述:标记一个布尔值必须为true或false。
- 示例:
@AssertTrue private boolean isValid; @AssertFalse private boolean isNotValid;
@Future
- 描述:标记一个日期必须是在未来某个时间点之后。
- 示例:
@Future private Date expiryDate;
@Past
- 描述:标记一个日期必须是在过去某个时间点之前。
- 示例:
@Past private Date purchaseDate;
这些注解通常与验证框架(如Hibernate Validator)一起使用,以在运行时验证对象的属性。
3. 注解使用
- 在项目的
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>
在实体类中使用上述注解,代码如下:
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; }
控制器类使用验证,代码如下:
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); } }
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); } }
定义全局异常处理类,我们在全局异常处理类中使用
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. 使用分组验证
- 用于插入记录时的分组验证,代码如下:
package com.yyqq.demo.interceptor;
import javax.validation.groups.Default;
public interface Insert extends Default {
}
- 用于更新记录时的分组验证,代码如下:
package com.yyqq.demo.interceptor;
import javax.validation.groups.Default;
public interface Update extends Default {
}
- 在实体类中进行分组标记,代码如下:
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;
}
- 控制器类使用分组验证
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. 自定义验证注解
除了框架自带的注解,平时的工作中可能需要我们自定义验证注解处理特定的业务需求。
- 定义注解
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 {};
}
- 定义验证器类
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}$";
@Override
public boolean isValid(String s, ConstraintValidatorContext context) {
boolean result = false;
try {
result = Pattern.matches(REGEX, s);
} catch (Exception e) {
System.out.println("验证手机号格式时发生异常,异常信息:" + e);
}
return result;
}
}
- 实体类使用注解
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 = "手机号格式不正确")
@Phone
private String phone;
}
6. @Valid与@Validated的区别
用于参数校验的注解通常有两个:@Valid
和@Validated
。它们的区别有如下几点:
区别 | @Valid | @Validated |
---|---|---|
来源 | @Valid 是Java标准注解 | @Validated 是Spring框架定义的注解。 |
是否支持分组验证 | 不支持 | 支持 |
使用位置 | 构造函数、方法、方法参数、成员属性 | 类、方法、方法参数,不能用于成员属性 |
是否支持嵌套校验 | 支持 | 不支持 |
相关文章
Springboot中如何记录好日志
springboot项目如何配置日志,日志门面和日志实现的区别是什么,如何通过日志切面将日志和代码解耦,这里都有分享。
编程日记 2024/02/27 21:34:27
Go 是否有三元运算符?Rust 和 Python 是怎么做的?
本文主要就 Go 中三元运算符展开讨论,从简单if-else语句、到基于匿名函数的单行表达式、及泛型抽象 If 函数等方式来实现类似的功能。当然,我没有建议使用这些方式,在没有内置支持的情况下,if-else的写法就挺好的。Go 中如何实现三元运算符?Rust 和 Python 是怎么做的?
编程日记 2024/02/21 09:46:19
Python3基础之import和from import的用法和区别
一个 python 的文件就叫做模块(module),如 xxx.py。模块就是一组功能的集合体,我们的程序可以导入模块来复用模块里的功能。
编程日记 2024/02/20 22:34:44
Mac 版 Excel 和 Windows 版 Excel的区别
它提供了丰富的功能和工具,包括公式、函数、图表和数据透视表等,帮助用户高效地处理和管理大量数据。例如,VBA 中的扩展 ASCII 字符在 MacOS 中通常有所不同,某些宏键盘快捷键似乎是保留的,VBA 动画仅适用于 Win 版 Excel ,并且右键单击上下文菜单无法使用 Mac 版 Excel 中的 VBA 进行编辑。对于许多高级用户来说,使用数据透视表和数据透视图执行数据分析和可视化数据的能力是 Excel 最有价值的功能之一,因此在确定哪个版本的 Excel 最适合您时需要考虑这一基本功能。
编程日记 2024/02/13 19:59:11
树莓派4B(Raspberry Pi 4B)使用docker搭建springBoot/springCloud服务
树莓派4B(Raspberry Pi 4B)使用docker搭建springBoot/springCloud服务
编程日记 2024/02/13 19:58:42
Python和Java的区别(不断更新)
运行效率:一般来说,Java的运行效率要高于Python,这主要是因为Java是编译型语言,其代码在执行前会进行预编译,而Python是解释型语言,边解释边执行。而Python没有类似的强大虚拟机,但它的核心是可以很方便地使用C语言函数或C++库,这使得Python可以轻松地与底层硬件进行交互。**类型系统:**Java是一种静态类型语言,所有变量需要先声明(类型)才能使用,且类型在编译时就已经确定。总的来说,Python和Java各有其优势和特点,选择哪种语言取决于具体的项目需求、开发环境以及个人偏好。
编程日记 2024/02/11 20:37:25
梯度是什么,为什么联邦学习传递这个就可以更新模型?
梯度是数学和机器学习领域中的一个基本概念,它表示的是一个函数在给定点处沿着不同方向的变化率或斜率。在多维空间中,梯度是一个向量,指向函数增长最快的方向,并且其大小表示在该方向上函数增长的速度。
编程日记 2024/02/08 18:09:30
VSCode python插件:找不到自定义包导致语法解析失败
vscode的默认python插件可没法聪明到根据这句话去找这个包,这就会导致后续代码中使用了这个库的部分无法享受自动补全、语法高亮等功能的便利性
编程日记 2024/02/07 09:14:30
虚拟机Windows Server 2016 安装 MySQL8
在虚拟机Windows Server 2016 中 安装MySQL8.0 并通过本机Navicat远程连接
编程日记 2024/02/04 09:56:57
SpringBoot security 安全认证(二)——登录拦截器
本节内容:实现登录拦截器,除了登录接口之外所有接口访问都要携带Token,并且对Token合法性进行验证,实现登录状态的保持。核心内容:1、要实现登录拦截器,从Request请求中获取token,从缓存中获取Token并验证登录是否过期,若验证通过则放行;2、实现对拦截器配置,SpringBoot 安全模块使用HttpSecurity 来完成请求安全管理。
编程日记 2024/02/04 09:56:30
Kafka常见生产问题详解
比如,在原有Topic下,可以调整Producer的分区策略,让Producer将后续的消息更多的发送到新增的Partition里,这样可以让各个Partition上的消息能够趋于平衡。思路是可行的,但是重试的次数,发送消息的数量等都是需要考虑的问题。PageCache缓存中的消息是断电即丢失的。因为如果业务逻辑异步进行,而消费者已经同步提交了Offset,那么如果业务逻辑执行过程中出现了异常,失败了,那么Broker端已经接收到了消费者的应答,后续就不会再重新推送消息,这样就造成了业务层面的消息丢失。
编程日记 2024/02/02 14:04:11
Zookeeper分布式队列实战
ZooKeeper实现队列步骤1.创建队列根节点:在Zookeeper中创建一个持久节点,用作队列的根节点。所有队列元素的节点将放在这个根节点下。2.实现入队操作:当需要将一个元素添加到队列时,可以在队列的根节点下创建一个临时有序节点。节点的数据可以包含队列元素的信息。3.实现出队操作:当需要从队列中取出一个元素时,先获取根节点下的所有子节点。再找到具有最小序号的子节点,获取该节点的数据,删除该节点,然后返回节点的数据。
编程日记 2024/02/01 14:42:13
解决Linux环境下gdal报错:ERROR 4: `/xxx.hdf‘ not recognized as a supported file format.
题外话:我发现linux系统和Windows系统下面,库的版本是有差异的。比如我的本机Windows上装的是gdal3.2.3和numpy1.19.1,linux服务器上装的却是gdal3.0.2和numpy1.21.5。这个是很常见的回复,网上许多回答都说低版本的 gdal 不支持 hdf5,让你重装高版本的gdal。我之前用pip安装了whl,暴力装上了,但用的时候就会有问题。安装了不冲突的gdal之后,就成功打开文件啦~一开始我是抱着试试的心态,用conda,不用pip,重新安装了一下我的gdal。
编程日记 2024/01/30 14:36:06
使用Opencv-python库读取图像、本地视频和摄像头实时数据
Python中使用OpenCV读取图像、本地视频和摄像头数据很简单,首先需要安装Python,然后安装Opencv-python库然后在PyCharm或者VScode等IDE中输入对应的Python代码。
编程日记 2024/01/28 17:48:47
SpringBoot:详解Bean生命周期和作用域
前面我们讲诉了将Bean正确地装配到IoC容器,却未讲诉IoC如何装配和销毁Bean。本篇文章主要讲诉一下Bean的生命周期和作用域。以上就是Bean生命周期和作用域的讲解。
编程日记 2024/01/22 09:39:43
HarmonyOS4.0系统性深入开发19进程模型概述
HarmonyOS通过CES(Common Event Service,公共事件服务)为应用程序提供订阅、发布、退订公共事件的能力。公共事件从系统角度可分为:系统公共事件和自定义公共事件。系统公共事件:CES内部定义的公共事件,只有系统应用和系统服务才能发布,例如HAP安装,更新,卸载等公共事件。目前支持的系统公共事件详见系统公共事件列表。自定义公共事件:应用自定义一些公共事件用来实现跨进程的事件通信能力。公共事件按发送方式可分为:无序公共事件、有序公共事件和粘性公共事件。
编程日记 2024/01/15 09:47:24
详解动态网页数据获取以及浏览器数据和网络数据交互流程-Python
动态网页是一种在用户浏览时实时生成或变化的网页。。相比之下,动态网页可以根据用户的互动、请求或其他条件在浏览器端或服务器端生成新的内容。而且现在的网页一般都是采用前后端分离的架构,前端负责展示和用户交互,后端负责数据处理。这种架构使得前端可以更加灵活地实现动态内容的加载和展示。所以说以后想要获取到数据,动态网页数据获取会成为我们主流获取网页数据的技术。所以在动态网页数据获取这方面我们需要下足功夫了解动态网页数据交互形式、数据存储访问模式等方方面面的知识,我们才好更加灵活的获取到数据。
编程日记 2024/01/13 17:46:53
学习如何使用 Python 连接 MongoDB: PyMongo 安装和基础操作教程
Python 可以用于数据库应用程序。最流行的 NoSQL 数据库之一是 MongoDB MongoDB MongoDB 将数据存储在类似 JSON 的文档中,使数据库非常灵活和可扩展。 您可以在 MongoDB 官网 上下载免费的 MongoDB 数据库 PyMongo Python 需要一个 M
编程日记 2024/01/13 10:25:09
Linux(Ubantu)交叉编译生成windows(32位,64位)可执行程序和库
与 mingw32 相比,mingw-w64 提供了对 64 位 Windows 应用程序的支持,并且通常被认为是更现代和更新的工具。这个选项通常用于 Unix-like 系统的编译器,用以指导链接器在生成可执行文件时保留符号信息,以便支持运行时的符号解析(例如用于动态加载库).该选项对于 Windows 下的编译是无效的,通过。(能够解析windows平台的可执行程序) 则能直接允许直接在linux环境中运行我们生成的win32的可执行程序(包括验证win32平台的动态库).
编程日记 2024/01/11 16:30:59
【微信支付】springboot-java接入微信支付-JSAPI支付/查单/退款/发送红包(四)---发送红包
在发放现金红包之前,请确保你的资金充足。操作路径:【登录商户平台——>交易中心——>资金管理——>充值】和红包相关的参数,你可以在页面上自主设置和更改。操作路径如下:【登录商户平台——>产品中心——>现金红包——>产品设置】在使用现金红包之前,请前往开通现金红包功能。操作路径:【登录微信支付商户平台——>产品中心——>现金红包——>开通】至此,整个微信支付的教程基本结束了,如果有小伙伴有其他问题,欢迎留言或者私信。商户调用微信红包接口时,微信支付服务器会进行证书验证,请现在商户平台下载证书。
编程日记 2024/01/09 13:11:24
详解静态网页数据获取以及浏览器数据和网络数据交互流程-Python
在网站设计领域,基于纯HTML格式构建的网页通常定义为静态网页,这种类型的网页是早期网站建设的主要形式。对于网络爬虫来说,抓取静态网页中的数据相对较为简单,因为所需的所有信息都直接嵌入在网页的HTML代码里。然而,对于那些利用AJAX技术动态加载数据的网页,其数据并不总是直接出现在HTML代码中,这对爬虫的抓取工作造成了一定的难度。在静态网页的数据抓取过程中,Requests库显示出其卓越的实用性。这个库不仅功能全面,而且操作简洁直观。
编程日记 2024/01/05 10:50:34
如何使用Plex在Windows系统搭建个人媒体站点公网可访问
用手机或者平板电脑看视频,已经算是生活中稀松平常的场景了,特别是各种碎片时间(追剧下饭、地铁上刷剧等等),看个喜欢的视频必不可少。但不知道为什么,各大影音平台总能轮流占住热播剧,还限定很多剧只能会员观看,搞得我们总有交不完的会员费。此时,拥有一个私人影音媒体站点就显得很有必要。今天,笔者就为大家介绍,如何使用cpolar+Plex组合,在Windows系统上搭建一个全能的私人媒体影音站点。
编程日记 2024/01/03 11:55:55
SpringBoot解决前后端分离跨域问题:状态码403拒绝访问
前后端分离项目因为端口不一致造成状态码403异常,通过Spring Boot来解决此跨域问题
编程日记 2023/12/31 08:34:33
Jupyter Notbook+cpolar内网穿透实现公共互联网访问使用数据分析工作
在数据分析工作中,使用最多的无疑就是各种函数、图表、代码和说明文档,这些复杂的内容不仅让使用的人头晕脑胀,也让普通的聊天工具一脸蒙圈。沟通工具不给力,就没法协同办公,可数据分析又离不开多人配合,所以Jupyter Notebook就成为大部分数据工作人员的必备工具。正如之前所说,Jupyter Notebook很适应复杂内容的沟通,因此现在也在机器学习、深度学习和教育工作中获得广泛应用。但Jupyter Notebook也有缺陷,就是被局限在局域网范围。
编程日记 2023/12/29 09:56:39