java后端校验
Java 后端数据校验
一、概述
当我们想提供可靠的 API 接口,对参数的校验,以保证最终数据入库的正确性,是 必不可少 的活。比如下图就是 我们一个项目里 新增一个菜单校验 参数的函数,写了一大堆的 if else 进行校验,非常的不优雅,比起枯燥的 CRUD 来说,参数校验更是枯燥。
这只是一个创建菜单的校验,只需要判断菜单,菜单 url 以及菜单的父类 id 是否为空,上级菜单是否挂载正确,这样已经消耗掉了 30,40 行代码了,更不要说,管理后台这种参数贼多的接口。估计要写几百行校验代码了。
if(mapper.get("customerId") == null){return RespBean.error("customerId is null!");}if(mapper.get("name") == null){return RespBean.error("name is null!");}if(mapper.get("userName") == null){return RespBean.error("userName is null!");}//查询条件 判断用户名是否重复Map<String,Object> args = new HashMap<>();args.put("userName",mapper.get("userName").toString());args.put("customerId",mapper.get("customerId").toString());Integer findNum = userService.findUserNameIsExist(args);if(!Objects.equals(findNum, 0)){ //查询结果不为0return RespBean.error("用户名重复,请重新输入!");}int ret = userService.addUser(mapper);if(ret >= 0){return RespBean.ok("UserController add user success");}else{return RespBean.error("UserController add User ailed");}
二、注解
在开始入门之前,我们先了解下本文可能会涉及到的注解。javax.validation.constraints 包下,定义了一系列的约束( constraint )注解。共 22 个,如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HMb3Jaa9-1690357601543)(file:///C:\Users\ADMINI~1\AppData\Local\Temp\ksohtml16688\wps1.jpg)]
大致可以分为以下几类:
2.1 空和非空检查
@NotBlank :只能用于字符串不为 null ,并且字符串 #trim() 以后 length 要大于 0 。
@NotBlank(message = "文本不能为空")
private String text;
@NotEmpty :集合对象的元素不为 0 ,即集合不为空,也可以用于字符串不为 null 。
@NotEmpty(message = "密码不能为空")
private String password;
@NotNull :不能为 null 。
@NotNull(message = "用户名不能为null")
private String userName;
@Null :必须为 null 。
@Null(message = "精度必须为null")
private Double jd;
2.2 数值检查
@DecimalMax(value) :被注释的元素必须是一个数字,其值必须小于等于指定的最大值。
@DecimalMax(value = "50.5",message = "number1必须小于等于50.5")
private Double number1;
@DecimalMin(value) :被注释的元素必须是一个数字,其值必须大于等于指定的最小值。
@DecimalMin(value = "100.6",message = "number2必须大于等于100.6")
private Double number2;
@Max(value) :该字段的值只能小于或等于该值。不支持小数位判断
@Max(value = 100,message = "number7必须小于等于100")
private Long number7;
@Min(value) :该字段的值只能大于或等于该值。 不支持小数位判断
@Min(value = 60,message = "number6必须大于等于60")
private Integer number6;
@Digits(integer, fraction) :被注释的元素必须是一个数字,其值必须在可接受的范围内。
@Digits(integer = 4,fraction = 3,message = "整数位上限4,小数位上限3")
private Double number3;
@Positive :判断正数。
@Positive(message = "number4必须为正数")
private Double number4;
@PositiveOrZero :判断正数或 0 。
@PositiveOrZero(message = "number5必须为正数或0")
private Double number5;
@Negative :判断负数。
@Negative(message = "number8必须为负数")
private Double number8;
@NegativeOrZero :判断负数或 0 。
@NegativeOrZero(message = "number9必须为负数或0")
private Double number9;
2.3 Boolean 值检查
@AssertFalse :被注释的元素必须为 true 。
@AssertTrue(message = "b1只能为true")
private Boolean b1;
@AssertTrue :被注释的元素必须为 false 。
@AssertFalse(message = "b2只能为false")
private Boolean b2;
2.4 长度检查
@Size(max, min) :检查该字段的 size 是否在 min 和 max 之间,可以是字符串、数组、集合、Map 等。
@Size(max = 5,min = 2,message = "字符串长度在2-5之间")
private String str;
2.5 日期检查
@Future :被注释的元素必须是一个将来的日期。
@Future
private Date date1;
@FutureOrPresent :判断日期是否是将来或现在日期。
@FutureOrPresent
private Date date2;
@Past :检查该字段的日期是在过去。
@Past
private Date date3;
@PastOrPresent :判断日期是否是过去或现在日期。
@PastOrPresent
private Date date4;
2.6 其它检查
@Email :被注释的元素必须是电子邮箱地址。
@Email
private String email;
@Pattern(value) :被注释的元素必须符合指定的正则表达式。注解需要传的参数:一般默认就填入正则表达式正则表达式即可,但是java中字符串需要转义
@Pattern(regexp = "^\\d{15}|\\d{18}$", message = "身份证号码在15-18位")
private String cardNum;
2.7 Hibernate Validator 附加的约束注解
org.hibernate.validator.constraints 包下,定义了一系列的约束( constraint )注解。如下:
@Range(min=, max=) :被注释的元素必须在合适的范围内。只判断整数位数值
@Range(min = 10,max = 20,message = "数值范围在10-20之间")
private Double range;
@Length(min=, max=) :被注释的字符串的大小必须在指定的范围内。
@Length(min = 20,max = 25,message = "字符串长度在20-25之间")
private String str1;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ctYx6pQh-1690357601545)(file:///C:\Users\ADMINI~1\AppData\Local\Temp\ksohtml16688\wps2.jpg)]
2.8 @Valid 和 @Validated
@Valid 注解,是 Bean Validation 所定义,可以添加在普通方法、构造方法、方法参数、方法返回、成员变量上,表示它们需要进行约束校验。
@Validated 注解,是 Spring Validation 锁定义,可以添加在类、方法参数、普通方法上,表示它们需要进行约束校验。同时,@Validated 有 value 属性,支持分组校验。属性如下:
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Validated {/*** Specify one or more validation groups to apply to the validation step* kicked off by this annotation.* <p>JSR-303 defines validation groups as custom annotations which an application declares* for the sole purpose of using them as type-safe group arguments, as implemented in* {@link org.springframework.validation.beanvalidation.SpringValidatorAdapter}.* <p>Other {@link org.springframework.validation.SmartValidator} implementations may* support class arguments in other ways as well.*/Class<?>[] value() default {};}
对于初学的来说,很容易搞混 @Valid(javax.validation 包下) 和 @Validated (org.springframework.validation.annotation 包下)注解。两者大致有以下的区别:
| 名称 | Spring validation是否实现了声明式检验 | 是否支持嵌套校验 | 是否支持分组校验 |
|---|---|---|---|
| @Validated | 是 | 否 | 是 |
| @Valid | 否 | 是 | 否 |
总的来说,绝大多数场景下,我们使用 @Validated 注解即可。而在有嵌套校验的场景,我们使用 @Valid 注解添加到成员属性上。
三、快速入门
3.1 引入依赖
<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><!--在一些高版本springboot中默认并不会引入这个依赖,需要手动引入-->
<!-- <dependency><groupId>org.hibernate.validator</groupId><artifactId>hibernate-validator</artifactId><scope>compile</scope></dependency>--><!-- 保证 Spring AOP 相关的依赖包 --><dependency><groupId>org.springframework</groupId><artifactId>spring-aspects</artifactId></dependency><!--lombok相关 方便开发--><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><scope>provided</scope></dependency><!--knife4j接口文档 方便待会进行接口测试--><dependency><groupId>com.github.xiaoymin</groupId><artifactId>knife4j-spring-boot-starter</artifactId><version>3.0.3</version></dependency></dependencies>
1) @Valid 的使用
首先,需要在实体类的相关字段上添加需要校验的注解。
public class User {private String id; @NotBlank(message = "密码不能为空")private String password;@Min(value = 18,message = "未成年禁止入内") private Integer age;
}
其次,在controller层的方法的要校验的参数上添加@Valid注解
@PostMapping("/action/register")public Result registerByForm(@Valid @RequestBody User user){return userService.register(user);}
嵌套功能的使用
在检验 Country对象的 provinces字段时,在provinces字段上添加 @Valid 注解后,就可以检验 list 中的 provinces的属性是否符合要求;
否则只会检验 citys的集合大小是否大于1,不会校验集合中的 citys对象,比如 citys对象的 name 是否符合要求。
@GetMapping (value = "/test1")public RespBean test1(@RequestBody @Valid Country country) {log.info("country:"+country);return RespBean.sucess();}@Data
public class Country {@NotNull(message = "国家id不能为null")private Long countryId;@Valid@Size(min = 1,max = 2,message = "省份数量在1-2之间")private List<Province> provinces;@Size(min = 1,message = "城市大于1")private List<City> citys;}@Data
public class Province {@NotNull(message = "省份id不能为null")private Long id;@NotBlank(message = "省份名称不能为空")private String name;}@Data
public class City {@NotNull(message = "城市id不能为null")private Long id;@NotBlank(message = "城市名称不能为空")private String name;}
2)@Validated 的使用
@Validated 是 @Valid 的一次封装,在@Valid的基础上增加了分组以及验证排序的功能。
分组功能的使用
当一个实体类需要多种验证方式时,比如:添加时需要对姓名进行非空验证,修改时需要对id进行验证,而添加时就不需要对id进行验证。
首先,定义两个分组的接口:
public interface Add{
}public interface Update{
}
其次,在实体类上使用@Validated的分组功能。
@Data
public class Person {@NotNull(groups = Update.class, message = "更新时候id不能为空")private Long id;@NotEmpty(groups = Add.class, message = "姓名不能为空")private String name;
}在controller中,使用分组进行接口验证。
@RestController
@Slf4j
public class VerifyController {@PostMapping(value = "/validated/add")public void add(@Validated(value = Add.class) @RequestBody Person person) {...}@PostMapping(value = "/validated/update")public void update(@Validated(value = Update.class) @RequestBody Person person) {...}
}
3)定义全局异常处理,才能返回参数校验信息
异常共四种
BindException:处理所有RequestParam注解数据验证异常
MethodArgumentNotValidException:处理所有RequestBody注解参数验证异常
ConstraintViolationException
UnexpectedTypeException
@RestControllerAdvice
@Slf4j
public class GlobalDefaultExceptionHandler {/*** 处理所有RequestBody注解参数验证异常* @param exception* @return*/@ExceptionHandler(MethodArgumentNotValidException.class)public RespBean methodArgumentNotValidException(MethodArgumentNotValidException exception){//封装需要返回的错误信息List<FieldError> fieldErrors = exception.getBindingResult().getFieldErrors();log.error("参数绑定异常,ex = {}", fieldErrors.get(0).getDefaultMessage());return RespBean.error(fieldErrors.get(0).getDefaultMessage());}/*** 处理所有RequestParam注解数据验证异常* @param e* @return*/@ExceptionHandler(BindException.class)public RespBean validationBodyException(BindException e){e.printStackTrace();//打印校验住的所有的错误信息StringBuilder sb = new StringBuilder("参数错误:[");List<ObjectError> list = ((BindException) e).getAllErrors();for (ObjectError item : list) {sb.append(item.getDefaultMessage()).append(',');}sb.deleteCharAt(sb.length()-1);sb.append(']');String msg = sb.toString();return RespBean.error(msg);}@ExceptionHandler(UnexpectedTypeException.class)public RespBean unexpectedTypeException(UnexpectedTypeException exception){//封装需要返回的错误信息log.error("参数绑定异常,ex = {}", exception.getMessage());return RespBean.error(exception.getMessage());}@ExceptionHandler(value = ConstraintViolationException.class)public RespBean ConstraintViolationExceptionHandler(ConstraintViolationException ex) {Set<ConstraintViolation<?>> constraintViolations = ex.getConstraintViolations();Iterator<ConstraintViolation<?>> iterator = constraintViolations.iterator();List<String> msgList = new ArrayList<>();while (iterator.hasNext()) {ConstraintViolation<?> cvl = iterator.next();msgList.add(cvl.getMessageTemplate());}return RespBean.error(msgList.toString());}
}
补充:分组功能使用多个组检验数据
contoller层使用{}添加多个组别
@PostMapping(value = "/validated/del")public RespBean dealMore(@Validated(value = {Add.class,Update.class}) @RequestBody Person person) {return RespBean.sucess();}
自定义注解
1-添加自定义注解类接口
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(validatedBy = {MobileValidator.class})
public @interface MobileCheck {boolean required() default true;String message() default "手机号码格式有误!";Class<?>[] groups() default {};Class<? extends Payload>[] payload() default {};
}
2-实现自定义校验器类
/** 手机号校验器* */
public class MobileValidator implements ConstraintValidator<MobileCheck, String> {private boolean required = false;private static final Pattern mobile_pattern = Pattern.compile("1\\d{10}");public static boolean isMobile(String src){if (StringUtils.isEmpty(src)){return false;}Matcher matcher = mobile_pattern.matcher(src);return matcher.matches();}@Overridepublic void initialize(MobileCheck isMobile) {required = isMobile.required();}@Overridepublic boolean isValid(String value, ConstraintValidatorContext context) {if (required) { //如果必须(不能为空),进行校验return isMobile(value);} else { //如果不必须,非空进行校验if (StringUtils.isEmpty(value)) {return true;}return isMobile(value);}}
}
3-使用
@Data
public class Country {@MobileCheckprivate String phone;}@PostMapping(value = "/checkPhone")
public RespBean checkPhone(@Valid @RequestBody Country country) {return RespBean.sucess();
}
}
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {if (required) { //如果必须(不能为空),进行校验return isMobile(value);} else { //如果不必须,非空进行校验if (StringUtils.isEmpty(value)) {return true;}return isMobile(value);}
}
}
3-使用
@Data
public class Country {
@MobileCheck
private String phone;
}
@PostMapping(value = “/checkPhone”)
public RespBean checkPhone(@Valid @RequestBody Country country) {
return RespBean.sucess();
}
相关文章:
java后端校验
Java 后端数据校验 一、概述 当我们想提供可靠的 API 接口,对参数的校验,以保证最终数据入库的正确性,是 必不可少 的活。比如下图就是 我们一个项目里 新增一个菜单校验 参数的函数,写了一大堆的 if else 进行校验,…...
PowerPoint如何修改“默认保存路径”?
很多时候,我们做好PPT后都要保存,一般会保存在创建PPT的文件夹里,或者另外设置保存的路径。 如果经常需要制作PPT,又不想每次都要重新选择保存位置,我们可以创建或修改“默认保存路径”,这样每次关闭PPT后…...
【PMP】有没有项目经理能看得懂这九张图?求挑战
这九张图,全是圈圈我的肺腑之言啊!谁痛谁知道! 做技术时,就想着30岁就转管理,管理岗位赚得多,结果发现全是烟雾弹。 做技术和代码打交道,做管理跟人打交道。天天开不完的会、说不完的话…...
ES6学习记录—自己记录一直更新版
1. 什么是ECMA 全称:European computer manufacturers association欧洲计算机制造联合会; 2、它的标准名单中的:ECMA — 262脚本语言的规范:规范化脚本语言,叫ECMAScript ( 一定要记住);像ES5 ES6就是这样来的…...
linux操作gpio的一些记录
在linux里面使用GPIO的一些知识点记录如下: 一、驱动里面操作GPIO 在linux内核里面如果 pinctrl 子系统将一个 PIN 复用为 GPIO 的话,那么就可以用gpio 子系统提供的 API 函数操做gpio,比如设置 GPIO为输入输出,读取 GPIO 的值等…...
目前新能源汽车充电桩的发展受到哪些不利因素的影响?
目前新能源汽车充电桩的发展受到哪些不利因素的影响? 一是安装难,很多老旧小区没有充电桩配套施工规范,充电桩建设比较难,受到充电容量不足电表箱供电等局限性的制约,同时缺乏充电桩配套设施的统一规划,小区内只能安装…...
jenkins
Gitlab添加钩子 测试钩子 添加完成后,下面会出现钩子选择。点击test中的,push event。 出现successful,既添加成功。 如果添加失败,报错,更改Network...
基于深度学习的图像分割技术探究
导言: 图像分割是计算机视觉领域的重要任务,旨在将图像划分为不同的语义区域,实现对图像中感兴趣物体的定位和提取。深度学习作为图像分割的新兴技术,通过卷积神经网络(CNN)等模型,取得了显著的…...
【c++】vector的使用与模拟实现
🚀write in front🚀 📜所属专栏:初阶数据结构 🛰️博客主页:睿睿的博客主页 🛰️代码仓库:🎉VS2022_C语言仓库 🎡您的点赞、关注、收藏、评论,是对…...
记录安装stable diffusion webui时,出现的gfpgan安装卡住的问题
参考链接:(145条消息) 使用stable diffusion webui时,安装gfpgan失败的解决方案(windows下的操作)_新时代原始人的博客-CSDN博客...
【开发环境】Windows下搭建TVM编译器
关于搭建TVM编译器的官方文档:Install from Source — tvm 0.14.dev0 documentation (apache.org) 1. 安装Anaconda 首先我们需要安装Anaconda,因为其中包含着我们所需要的各类依赖: 进入Anaconda官网https://www.anaconda.com/products/d…...
了解Unity编辑器之组件篇Video(二)
Video Player组件:用于在游戏中播放视频的组件。它提供了一系列属性来控制视频的播放、显示和交互。 1.Source(视频源):用于指定视频的来源。可以选择两种不同的视频源类型: (1)Vieo Clip&#…...
安全杂记 - 状态码,DNS,编码
目录 1.状态码2.DNS解析过程3.URL编码4.HTML实体编码5.FORM表单 1.状态码 200 - 请求成功 301 - 资源(网页等)被永久转移到其它URL 302 - 临时移动。与301类似。但资源只是临时被移动。客户端应继续使用原有URI 304 - 未修改。所请求的资源未修改&#…...
微信小程序 Page页面
新建页面只需要在app.json配置好路径,编译器自动新增了页面 项目首页,在app.json哪个页面是第一位,哪个页面就是小程序首页...
C语言实现基于Linux,epoll和多线程的WebServer服务器
代码结构: Server.h 头文件,对函数进行了声明 #pragma once #include<stdio.h> // 新建一个用于TCP监听的socket文件描述符,并返回 int initListenFd(unsigned short port);// 启动epoll int epollRun(int lfd);// accept建立连接 vo…...
微信小程序数字键盘(仿微信转账键盘)
微信小程序input自带数字输入键盘,不过是直接调用的系统键盘,无法个性化。 代码中使用使用了Vant WeappVant UI小程序版,这里就不介绍相关安装说明了,大家自行安装Vant Weapp。 json 用到的组件 {"usingComponents": …...
mac电脑强大的解压缩软件BetterZip 5.3.4 for Mac中文版及betterzip怎么压缩
BetterZip 5.3.4 for Mac 是Mac系统平台上一款功能强大的文件解压缩软件,不必解压就能快速地检查压缩文档。它能执行文件之间的合并并提供密码。使用它,用户可以更快捷的向压缩文件中添加和删除文件。它支持包括zip、gz、bz、bz2、tar、tgz、tbz、rar、7…...
Llama 2 来袭 - 在 Hugging Face 上玩转它
🤗 宝子们可以戳 阅读原文 查看文中所有的外部链接哟! 引言 今天,Meta 发布了 Llama 2,其包含了一系列最先进的开放大语言模型,我们很高兴能够将其全面集成入 Hugging Face,并全力支持其发布。Llama 2 的社…...
linux操作历史history定制
history记录 Linux中历史操作记录history是一个很有用的功能,有时忘记了,翻翻以前的命令,十分方便。 # 展示所有历史记录 history # 筛选历史记录 history | grep nginx # 清除全部记录 -c history -c # 指定删除某一行,15是行号 history -…...
微信小程序 wx.showModal
微信小程序--wx.showModal_海轰Pro的博客-CSDN博客...
应用升级/灾备测试时使用guarantee 闪回点迅速回退
1.场景 应用要升级,当升级失败时,数据库回退到升级前. 要测试系统,测试完成后,数据库要回退到测试前。 相对于RMAN恢复需要很长时间, 数据库闪回只需要几分钟。 2.技术实现 数据库设置 2个db_recovery参数 创建guarantee闪回点,不需要开启数据库闪回。…...
STM32标准库-DMA直接存储器存取
文章目录 一、DMA1.1简介1.2存储器映像1.3DMA框图1.4DMA基本结构1.5DMA请求1.6数据宽度与对齐1.7数据转运DMA1.8ADC扫描模式DMA 二、数据转运DMA2.1接线图2.2代码2.3相关API 一、DMA 1.1简介 DMA(Direct Memory Access)直接存储器存取 DMA可以提供外设…...
html-<abbr> 缩写或首字母缩略词
定义与作用 <abbr> 标签用于表示缩写或首字母缩略词,它可以帮助用户更好地理解缩写的含义,尤其是对于那些不熟悉该缩写的用户。 title 属性的内容提供了缩写的详细说明。当用户将鼠标悬停在缩写上时,会显示一个提示框。 示例&#x…...
JAVA后端开发——多租户
数据隔离是多租户系统中的核心概念,确保一个租户(在这个系统中可能是一个公司或一个独立的客户)的数据对其他租户是不可见的。在 RuoYi 框架(您当前项目所使用的基础框架)中,这通常是通过在数据表中增加一个…...
嵌入式学习笔记DAY33(网络编程——TCP)
一、网络架构 C/S (client/server 客户端/服务器):由客户端和服务器端两个部分组成。客户端通常是用户使用的应用程序,负责提供用户界面和交互逻辑 ,接收用户输入,向服务器发送请求,并展示服务…...
A2A JS SDK 完整教程:快速入门指南
目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库ÿ…...
LangChain知识库管理后端接口:数据库操作详解—— 构建本地知识库系统的基础《二》
这段 Python 代码是一个完整的 知识库数据库操作模块,用于对本地知识库系统中的知识库进行增删改查(CRUD)操作。它基于 SQLAlchemy ORM 框架 和一个自定义的装饰器 with_session 实现数据库会话管理。 📘 一、整体功能概述 该模块…...
Xela矩阵三轴触觉传感器的工作原理解析与应用场景
Xela矩阵三轴触觉传感器通过先进技术模拟人类触觉感知,帮助设备实现精确的力测量与位移监测。其核心功能基于磁性三维力测量与空间位移测量,能够捕捉多维触觉信息。该传感器的设计不仅提升了触觉感知的精度,还为机器人、医疗设备和制造业的智…...
GAN模式奔溃的探讨论文综述(一)
简介 简介:今天带来一篇关于GAN的,对于模式奔溃的一个探讨的一个问题,帮助大家更好的解决训练中遇到的一个难题。 论文题目:An in-depth review and analysis of mode collapse in GAN 期刊:Machine Learning 链接:...
iOS 项目怎么构建稳定性保障机制?一次系统性防错经验分享(含 KeyMob 工具应用)
崩溃、内存飙升、后台任务未释放、页面卡顿、日志丢失——稳定性问题,不一定会立刻崩,但一旦积累,就是“上线后救不回来的代价”。 稳定性保障不是某个工具的功能,而是一套贯穿开发、测试、上线全流程的“观测分析防范”机制。 …...
