Spring Validation 接口入参校验

一、前言

  • JSR 是 Java Specification Requests 的缩写,含义为 JAVA 规范提案。

  • JSR 303 - Bean Validation 规范, 正是一套基于 JavaBean 参数校验的标准。

  • Hibernate Validator 是 JSR 303 的实现,它提供了 JSR 303 规范中所有约束(constraint)的实现,同时也对其作出一些拓展。

  • Spring Validation 是对 Hibernate validation 的二次封装,用于支持 Spring MVC 参数校验。


😊 JSR 303 包含的注解:

验证注解

验证数据类型

说明

@Null

任意类型

元素值为 Null

@NotNull

任意类型

元素值不为 Null

@AssertTrue

Bool

元素为 true

@AssertFalse

Bool

元素为 false

@Min(value = 最小值)

BigDecimal、BigInteger、byte、short、int、long 等 Number 或 CharSequence (数字)子类型

元素值需大于等于指定值

@Max(value = 最大值)

BigDecimal、BigInteger、byte、short、int、long 等 Number 或 CharSequence (数字)子类型

元素值需小于等于指定值

@DecimalMin(value = 最小值)

BigDecimal、BigInteger、byte、short、int、long 等 Number 或 CharSequence (数字)子类型

元素值需大于等于指定值

@DecimalMax(value = 最大值)

BigDecimal、BigInteger、byte、short、int、long 等 Number 或 CharSequence (数字)子类型

元素值需小于等于指定值

@Size(min = 最小值, max = 最大值)

String、Collection、Array 等

元素值的字符长度/集合大小需在指定的区间范围内

@Digits(integer = 整数位数, fraction = 小数位数)

BigDecimal、BigInteger、byte、short、int、long 等 Number 或 CharSequence (数字)子类型

元素值整数位、小数位需小于对应指定值

@Past

DATE、Calendar、Time 等日期

元素值需在指定时间之前

@Future

DATE、Calendar、Time 等日期

元素值需在指定时间之后

@Pattern(regexp = 正则式, flag = 标志的模式)

String 等

元素值与指定的正则式匹配

😊hibernate.validator 扩展的注解:

验证注解

验证数据类型

说明

@NotBlank

String 等 CharSequence 子类型

元素值不为空串(字符串不为 Null 且去除首尾空格后长度不为 0)

@Email(regexp = 正则式, flag = 标志的模式)

String 等

元素值为电子邮箱格式,可通过 regexp、flag 属性指定格式

@Length(min = 最小值, max = 最大值)

String 等

元素值长度在指定区间范围内

@NotEmpty

String、Collection、Array 等

元素值不为 Null 且长度不为 0

@Range(min = 最小值, max = 最大值)

BigDecimal、BigInteger、byte、short、int、long 等 Number 或 CharSequence (数字)子类型

元素值在指定区间范围内

@URL

String 等

元素值必须时合法的URL

二、Spring Validation的使用

1、在项目pom.xml中引入依赖

<!-- JSR 303 -->
<dependency><groupId>javax.validation</groupId><artifactId>validation-api</artifactId>
</dependency><!-- 在 SpringBoot 项目中,若 SpringBoot 版本小于 2.3.x ,则此依赖已包含在 spring-boot-starter-web 中,无需添加额外依赖;
若 SpringBoot 版本大于 2.3.x ,则需手动引入依赖。-->
<!-- Hibernate Validator -->
<dependency><groupId>org.hibernate.validator</groupId><artifactId>hibernate-validator</artifactId>
</dependency>

2、 参数注解校验的使用场景

场景一:直接入参的校验

①在类上添加 @Validated 注解,否则参数校验无法生效;

②入参中使用注解 @NotEmpty 等;

@Validated
@RestController
@RequestMapping("/user")
public class UserController {@GetMapping("/saveUserName")public String saveUserInfo(@NotEmpty String userName) {System.out.println("userName:"+ userName +",保存成功");return "success";}
}

测试:

【请求URL】:
http://192.168.1.7:27100/user/saveUserName?userName=【执行结果】:
严重: Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is javax.validation.ConstraintViolationException: saveUserInfo.userName: 不能为空] with root cause
javax.validation.ConstraintViolationException: saveUserInfo.userName: 不能为空at org.springframework.validation.beanvalidation.MethodValidationInterceptor.invoke(MethodValidationInterceptor.java:116)at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:688)
……

场景二:对象模型属性的校验

1、创建 User对象

@Data
public class User {private Integer id;@NotEmpty(message = "用户名不能为空")private String userName;private Integer age;@NotNull(message = "用户密码不能为空")@Size(min = 5, max = 10,message = "密码长度必须是5-10个字符")private String password;
}

2、全局异常捕获

@RestControllerAdvice
public class GlobalExceptionHandler {/*** 方法直接入参校验** @param ex* @return*/@ExceptionHandler(ConstraintViolationException.class)public BaseResult resolveConstraintViolationException(ConstraintViolationException ex) {Set<ConstraintViolation<?>> constraintViolations = ex.getConstraintViolations();if (!CollectionUtils.isEmpty(constraintViolations)) {String errorMessage = constraintViolations.stream().map(ConstraintViolation::getMessage).collect(Collectors.joining(", "));return BaseResult.fail(ResultEnum.ILLEGAL_PARAMETER, errorMessage);}return BaseResult.fail(ResultEnum.ILLEGAL_PARAMETER, ex.getMessage());}/*** 对象模型属性校验** @param ex* @return*/@ExceptionHandler(MethodArgumentNotValidException.class)public BaseResult resolveMethodArgumentNotValidException(MethodArgumentNotValidException ex) {List<ObjectError> allErrors = ex.getBindingResult().getAllErrors();if (!CollectionUtils.isEmpty(allErrors)) {String errorMessage = allErrors.stream().map(ObjectError::getDefaultMessage).collect(Collectors.joining(", "));return BaseResult.fail(ResultEnum.ILLEGAL_PARAMETER, errorMessage);}return BaseResult.fail(ResultEnum.ILLEGAL_PARAMETER, ex.getMessage());}
}

3、通用返回结构体

@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
public class BaseResult<T> {private Integer code;private String message;private T data;public static <T> BaseResult<T> success() {return new BaseResult(ResultEnum.SUCCESS.getCode(), ResultEnum.SUCCESS.getMsg(), new JSONObject());}public static <T> BaseResult<T> fail(ResultEnum resultEnum, T data) {return fail(resultEnum.getCode(), resultEnum.getMsg(), data);}@Overridepublic String toString() {return "BaseResult{" +"code=" + code +", message='" + message + '\'' +", data=" + data +'}';}
}

4、错误提示码枚举类

/*** 错误提示码*/
public enum ResultEnum {/** 状态码:未知错误 **/UNKNOWN_ERROR(-1, "未知错误"),/** 状态码:成功 **/SUCCESS(0, "成功"),/** 状态码:失败 **/FAIL(1,"失败"),/** 状态码:非法参数 **/ILLEGAL_PARAMETER(2,"非法参数");private Integer code;private String msg;ResultEnum(Integer code, String msg) {this.code = code;this.msg = msg;}public Integer getCode() {return code;}public String getMsg() {return msg;}}

5、暴露的接口

① 在类上添加 @Validated 注解,否则参数校验无法生效;

② 在入参实体前添加 @Validated 注解;

@Validated
@RestController
@RequestMapping("/user")
public class UserController {@PostMapping("/saveUserInfo")public String saveUserInfo(@RequestBody @Validated User user) {System.out.println("userName:"+ user.getUserName() +",保存成功");return "success";}
}

6、测试:

【请求URL】:http://192.168.1.7:27100/user/saveUserInfo
【请求方式】:POST
【请求参数】:
{"userName":"张三"
}【执行结果】:
{"code": 2,"message": "非法参数","data": "用户密码不能为空"
}

场景三:参数注解校验的分组验证

1、新建分组接口

/*** 分组接口:新增*/
public interface Add {}/*** 分组接口:更新*/
public interface Update {}

2、在校验注解中使用 groups 属性标明分组

@Data
public class User {@NotNull(message = "更新数据时,主键id不能为空", groups = Update.class)private Integer id;@NotEmpty(message = "用户名不能为空")private String userName;private Integer age;@NotNull(message = "用户密码不能为空")@Size(min = 5, max = 10,message = "密码长度必须是5-10个字符")private String password;
}

【注】需要注意的是实体类中所有字段注解校验规则默认属于 Default 分组,接口入参 @Validated 注解默认检验 Default 分组的校验规则。当接口入参使用 @Validated 显式声明非默认分组时,实体类中所有未显式声明分组的注解校验将不会生效。

3、处理器方法

@Validated
@RestController
@RequestMapping("/user")
public class UserController {@PostMapping("/updateUserInfo")public String updateUserInfo(@RequestBody @Validated({Update.class}) User user) {System.out.println("id:" + user.getId() + ",userName:"+ user.getUserName() +",更新成功");return "success";}
}

4、测试

【请求URL】:http://192.168.1.7:27100/user/updateUserInfo
【请求方式】:POST
【请求参数】:
{"id":1,"userName":"李四"
}【执行结果】:
id:1,userName:李四,更新成功

【注】入参中没有传“密码”,也没有校验,若默认属于 Default 分组的字段也需要校验,可以写成:

public String updateUserInfo(@RequestBody @Validated({Update.class, Default.class}) User user) {

场景四:级联校验

1、在实体类模型中,可能会存在集合类型或实体类型的成员变量,该成员中的字段仍然需要校验。

在要校验的对象类型的属性/ list上使用 @Valid 注解:

@Data
public class User {@NotNull(message = "更新数据时,主键id不能为空", groups = Update.class)private Integer id;@NotEmpty(message = "用户名不能为空")private String userName;private Integer age;@NotNull(message = "用户密码不能为空")@Size(min = 5, max = 10,message = "密码长度必须是5-10个字符")private String password;@Validprivate List<Car> cars;
}@Data
public class Car {@NotNull(message = "车牌号码不能为空")private String plateCode; @NotNull(message = "车牌颜色不能为空")private String plateColor;
}

2、处理器方法

@Validated
@RestController
@RequestMapping("/user")
public class UserController {@PostMapping("/saveUserInfo")public String saveUserInfo(@RequestBody @Validated User user) {System.out.println("userName:"+ user.getUserName() +",保存成功");return "success";}
}

3、测试

【请求URL】:http://192.168.1.7:27100/user/saveUserInfo
【请求方式】:POST
【请求参数】:
{"userName": "李四","password": "123456","cars": [{"plateCode": "京A0001","plateColor": "1"},{"plateCode": "京A0002"}]
}【执行结果】:
{"code": 2,"message": "非法参数","data": "车牌颜色不能为空"
}

场景五:自定义注释校验

如下:校验用户下车牌号码必须包含“京A”,否则返回提示;

1、自定义注释

@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER})
@Retention(RUNTIME)
@Documented
@Constraint(validatedBy = {MustContainKeyValidator.class})
public @interface MustContainKey {//默认错误信息String message() default "必须含有指定关键字";//分组Class<?>[] groups() default {};// 负载Class<? extends Payload>[] payload() default {};
}

2、真正的校验者类,实现 ConstraintValidator 接口

public class MustContainKeyValidator implements ConstraintValidator<MustContainKey,String> {@Overridepublic boolean isValid(String value, ConstraintValidatorContext context) {if (!StringUtils.isEmpty(value) && !value.contains("京A")) {// 获取默认提示消息String defaultConstraintMessageTemplate = context.getDefaultConstraintMessageTemplate();System.out.println(defaultConstraintMessageTemplate);// 禁用默认提示信息// context.disableDefaultConstraintViolation();//设置提示语// context.buildConstraintViolationWithTemplate("must contain key").addConstraintViolation();return false;}return true;}
}

3、在实体类的属性上添加该注释

@Data
public class User {@NotNull(message = "更新数据时,主键id不能为空", groups = Update.class)private Integer id;@NotEmpty(message = "用户名不能为空")private String userName;private Integer age;@NotNull(message = "用户密码不能为空")@Size(min = 5, max = 10,message = "密码长度必须是5-10个字符")private String password;@Validprivate List<Car> cars;
}@Data
public class Car {@MustContainKey@NotNull(message = "车牌号码不能为空")private String plateCode; @NotNull(message = "车牌颜色不能为空")private String plateColor;
}

4、测试

【请求URL】:http://192.168.1.7:27100/user/saveUserInfo
【请求方式】:POST
【请求参数】:
{"userName": "李四","password": "123456","cars": [{"plateCode": "京A0001","plateColor": "1"},{"plateCode": "浙C0002","plateColor": "2"}]
}
【执行结果】:
{"code": 2,"message": "非法参数","data": "必须含有指定关键字"
}

以上就是所有关于 Spring Validation 的介绍,感兴趣的同学欢迎点赞+收藏!

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

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

相关文章

【Java】一文搞懂生产者和消费者模型

阻塞队列的概念生产者消费者模式消息队列消息队列的作用 JDK中的阻塞队列实现阻塞队列实现生产者消费者模型 阻塞队列的概念 之前介绍过队列&#xff0c;是一种数据结构&#xff0c;先进先出FIFO。阻塞队列也满足队列的特性&#xff0c;不过有特殊之处&#xff1a; 入队元素时…

泪崩!测试面试技术面过了却挂在了——“谈谈你的职业生涯规划”

前不久&#xff0c;软件测试交流群里面有一个成员吐槽&#xff0c;说今天的面试技术已经面过了&#xff0c;可HR却问了她“未来的职业发展目标是什么&#xff1f;”然后&#xff0c;挂了&#xff01;这个问题我们平时在交流群里都有讲过&#xff0c;可是这丫头比较疯&#xff0…

kml或kmz文件用什么软件打开

下载安装 bigemap GIS office软件&#xff08;免费就可以) 2、 安装好下载的bigemap软件&#xff0c;直接将kml kmz拖到软件里面就打开了&#xff0c;或者左上角文件打开 选择 kml/kmz 然后选择你的文件 打开记性了。 BIGEMAP支持所有文件格式的打开和保存&#xff0c;如下图…

解读DXF文件

转自&#xff1a;http://blog.csdn.net/yingmutongxue/article/details/8226104 C语言代码读取DXF&#xff1a; http://www.docin.com/p-346248233.html?nb1 组码详解&#xff1a;http://www.doc88.com/p-21997575043.html DXF文件就是以文档的格式描述图形数据的。可以用写…

GS2972(3G-SDI)视频输出驱动调试

GS2972视频输出调试 GS2972的硬件初始化GS2972的驱动时序GS2972的驱动RTL代码GS2972输出彩条GS2972驱动易出bug GS2972的硬件初始化 GS2972是HD-SDI/3G-SDI视频、音频串化器。其使用非常简单&#xff0c;但是要想把该芯片驱动起来&#xff0c;真心不容易。需要了解相关视频标准…

如何在Moonbeam设置多重签名钱包,加固资产安全

Moonbeam Safe是以太坊上Safe&#xff08;先前名为Gnosis Safe&#xff09;的分叉。Safe于2018年正式推出&#xff0c;并发展成为了以太坊上知名的去中心化托管协议和集体资产管理平台。 Moonbeam Safe可用于创建多重签名Safe钱包&#xff0c;通过配置一个多签&#xff08;mul…

【Protobuf速成指南】Win/Centos7下Protobuf安装教程

文章目录 安装教程一、Windows1.1 下载编译器1.2 配置PATH1.3 其他依赖项 二、Centos72.1 安装必要的工具2.2 下载安装包2.3 安装 安装教程 以版本为V21.11为例说明 一、Windows 1.1 下载编译器 下载地址&#xff1a;链接&#xff0c;一直往下翻找到 V21.11版本 win用户根据…

去除迅雷极速版 提示升级的广告

转载于:https://www.cnblogs.com/x-huihui/p/10926954.html

迅雷精简版 4.0.0 Mac中文版

迅雷精简版是一款非常实用的下载工具&#xff0c;体积轻巧但是下载速度却依然很快&#xff0c;采用与浏览器结合的方式&#xff0c;让用户在享受极速下载模式的同时也能流畅上网&#xff0c;系统性能依然稳定&#xff0c;而且迅雷精简版没有广告&#xff0c;给用户更好的上网体…

迅雷精简版 for Mac 去除多余无用功能

推一款非常给力、速度又快的专业下载工具mac迅雷精简版&#xff0c;普通的非VIP迅雷软件往往会出现一些下载限速&#xff0c;体验上相对并不是很好&#xff0c;而迅雷极速精简版则更好的解决了这个问题&#xff0c;其界面清爽简约&#xff0c;无广告&#xff0c;下载速度快&…

[Android] 安卓迅雷带云盘内测版7.0 简洁 无广告 官方版

迅雷内测版本 界面 简洁 无广告 带云盘功能 我也不多说 大家都知道迅雷APP 版本德性 全是广告 直接上图 跟链接 对于我说 内测版本 属实香 看不到一点广告 下载地址: https://n802.com/file/349707-458153240 http://www.yimuhe.com/file-4770885.html http://www.369pan.c…

2015 年一月联考逻辑真题

2015 年一月联考逻辑真题 真题&#xff08;2015-26&#xff09; 26.晴朗的夜晚我们可以看到满天星斗&#xff0c;其中有些是自身发光的恒星&#xff0c;有些是自身不发光但可以反射附近恒星光的行星。恒星尽管遥远&#xff0c;但是有些可以被现有的光学望远镜“看到”。和恒星不…

python使用requests+excel进行接口自动化测试

在当今的互联网时代中&#xff0c;接口自动化测试越来越成为软件测试的重要组成部分。Python是一种简单易学&#xff0c;高效且可扩展的语言&#xff0c;自然而然地成为了开发人员的首选开发语言。而requests和xlwt这两个常用的Python标准库&#xff0c;能够帮助我们轻松地开发…

navicat与SQLyog的区别

在之前的学习中由于先学的SQL Server&#xff0c;后来才学的MySQL&#xff0c;导致我刚学习的时候冥冥之中感觉到那有点不对劲&#xff0c;但是又说不出来。通过进行深入的学习解除到了Navicat Premium和SQLyog这两个工具&#xff0c;才让我明白了MySQL与之前学习的内容是有所出…

usb2.0-crw出现黄色叹号

最近点开设备管理器发现了许多黄色感叹号&#xff1a; 解决方案&#xff1a; 进入电脑官网&#xff0c;我的是DELL&#xff0c;搜索"驱动与下载dell"&#xff0c;输入自己的电脑型号&#xff0c;在手动搜索的模块里找主板芯片组&#xff0c;下载并安装。

postgrsql 增加字段

alter table 表名 add 字段名 数据类型&#xff1b; 不支持指定位置增加列&#xff0c;增加的列在末尾。 实例:alter table crw_it.ncs_ccs_stmt_zdhk add stmt_no varchar; crw_it是模式名&#xff08;schema&#xff09;

STM32单片机蓝牙APP语音识别取暖器GSM短信超温报警

实践制作DIY- GC0141-蓝牙APP语音识别取暖器 基于STM32单片机设计---蓝牙APP语音识别取暖器 二、功能介绍&#xff1a; 电路&#xff1a;STM32F103C最小系统DS18B20温度传感器 多个按键 LCD1602显示器 1个串口语音识别模块1个5V 加热片 模拟加热蜂鸣器SIM800 GSM短信模块 HC0…

ubuntu下依靠guvcview使用摄像头

1.检验系统是否可以检测到设备 $ lsusb Bus 001 Device 040: ID 046d:0825 Logitech, Inc. Webcam C270 如果没有相关的信息说明系统的驱动未安装。 2.检验摄像头的端口 ls -la /dev/vid* crw-rw---- 1 root video 81, 0 Feb 12 2016 /dev/video0 crw-rw---- 1 root vid…

降本增效,StarRocks 在同程旅行的实践

作者&#xff1a;周涛 同程旅行数据中心大数据研发工程师 同程旅行是中国在线旅游行业的创新者和市场领导者。作为一家一站式平台&#xff0c;同程旅行致力于满足用户旅游需求&#xff0c;秉持 "让旅行更简单、更快乐" 的使命&#xff0c;主要通过包括微信小程序、AP…

CRC16浅析

CRC即循环冗余校验码&#xff08;Cyclic Redundancy Check&#xff09;&#xff0c;是数据通信领域中最常用的一种查错校验码。奇偶校验虽然简单&#xff0c;但是漏检率太高&#xff0c;而CRC则要低的多&#xff0c;所以大多数都是使用CRC来校验。CRC也称为多项式码。 任何一个…