【实战案例】JSR303统一校验与SpringBoot项目的整合
前后端分离项目中,当前前端请求后端接口的时候通常需要传输参数,对于参数的校验应该在哪一步进行校验?Controller中还是Service中?答案是都需要校验,只不过负责的板块不一样,Controller中通常校验请求参数的合法性,包括:必填项校验,数据格式校验,比如:是否为空,是否符合一定的日期格式等。Service中要校验的是业务规则相关的内容,比如:课程已经审核通过所以提交失败。Service中根据业务规则去校验通常因为业务各异所以一般无法提取成通用代码,Controller中则可以将校验的代码写成通用代码。
在JavaEE6规范中就定义了参数校验的规范JSR-303,它定义了Bean Validation,即对bean属性进行校验。同时SpringBoot也提供了JSR-303的支持,即spring-boot-starter-validation,底层使用Hibernate Validator,Hibernate Validator是Bean Validation 的参考实现。
接下来在Controller层使用spring-boot-starter-validation完成对请求参数的基本合法性进行校验。
首先在项目所属或依赖工程中添加相应依赖:
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId>
</dependency>
在javax.validation.constraints包下有很多校验注解用于定义校验规则。
Constraint | 详细信息 |
---|---|
@Null | 被注释的元素必须为 null |
@NotNull | 被注释的元素必须不为 null |
@AssertTrue | 被注释的元素必须为 true |
@AssertFalse | 被注释的元素必须为 false |
@Min(value) | 被注释的元素必须是一个数字,其值必须大于等于指定的最小值 |
@Max(value) | 被注释的元素必须是一个数字,其值必须小于等于指定的最大值 |
@DecimalMin(value) | 被注释的元素必须是一个数字,其值必须大于等于指定的最小值 |
@DecimalMax(value) | 被注释的元素必须是一个数字,其值必须小于等于指定的最大值 |
@Size(max, min) | 被注释的元素的大小必须在指定的范围内 |
@Digits(integer, fraction) | 被注释的元素必须是一个数字,其值必须在可接受的范围内 |
@Past | 被注释的元素必须是一个过去的日期 |
@Future | 被注释的元素必须是一个将来的日期 |
@Pattern(regex=) | 被注释的元素必须符合指定的正则表达式 |
@Valid | 被注释的元素需要递归验证 |
被注释的元素必须是电子邮箱地址 | |
@Length(min=下限, max=上限) | 被注释的字符串的大小必须在指定的范围内 |
@NotEmpty | 被注释的元素的必须非空并且size大于0 |
@NotBlank | 被注释的元素必须不为空且不能全部为’ '(空字符串) |
@Range(min=最小值, max=最大值) | 被注释的元素必须在合适的范围内 |
例如项目中存在一个接口用于创建课程:
@ApiOperation("新增课程")
@PostMapping("/course")
public CourseBaseInfoDto createCourseBase(@RequestBody AddCourseDto addCourseDto) {Long companyId = 10000L;CourseBaseInfoDto courseBase = courseBaseInfoService.createCourseBase(companyId, addCourseDto);return courseBase;
}
此接口使用AddCourseDto模型对象接收参数,可进入AddCourseDto类,在属性上添加校验规则。
package com.gavin.content.model.dto;import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.Size;
import java.math.BigDecimal;/*** @author Gavin* @description 添加课程dto* @date 2024/10/14*/
@Data
@ApiModel(value = "AddCourseDto", description = "新增课程基本信息")
public class AddCourseDto {@NotEmpty(message = "课程名称不能为空")@ApiModelProperty(value = "课程名称", required = true)private String name;@NotEmpty(message = "适用人群不能为空")@Size(message = "适用人群内容过少", min = 10)@ApiModelProperty(value = "适用人群", required = true)private String users;@ApiModelProperty(value = "课程标签")private String tags;@NotEmpty(message = "课程分类不能为空")@ApiModelProperty(value = "大分类", required = true)private String mt;@NotEmpty(message = "课程分类不能为空")@ApiModelProperty(value = "小分类", required = true)private String st;@NotEmpty(message = "课程等级不能为空")@ApiModelProperty(value = "课程等级", required = true)private String grade;@ApiModelProperty(value = "教学模式(普通,录播,直播等)", required = true)private String teachmode;@ApiModelProperty(value = "课程介绍")private String description;@ApiModelProperty(value = "课程图片", required = true)private String pic;@NotEmpty(message = "收费规则不能为空")@ApiModelProperty(value = "收费规则,对应数据字典", required = true)private String charge;@ApiModelProperty(value = "价格")private Float price;@ApiModelProperty(value = "原价")private Float originalPrice;@ApiModelProperty(value = "qq")private String qq;@ApiModelProperty(value = "微信")private String wechat;@ApiModelProperty(value = "电话")private String phone;@ApiModelProperty(value = "有效期")private Integer validDays;
}
上述程序中@NotEmpty表示属性不能为空,@Size表示限制属性内容的长短。
定义好校验规则还需要开启校验,在controller方法中添加@Validated注解,如下:
@ApiOperation("新增课程")
@PostMapping("/course")
public CourseBaseInfoDto createCourseBase(@RequestBody @Validated AddCourseDto addCourseDto) {//获取到用户所属机构的idLong companyId = 10000L;CourseBaseInfoDto courseBase = courseBaseInfoService.createCourseBase(companyId, addCourseDto);return courseBase;
}
如果校验出错Spring会抛出MethodArgumentNotValidException异常,为了使得报错信息更加明确,可以在统一异常处理器中捕获异常,解析出异常信息。在全局异常处理器中添加对该类异常的解析处理(全局异常处理类的建立见【实战案例】SpringBoot项目中异常处理通用解决方案),如下:
@ResponseBody
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public RestErrorResponse methodArgumentNotValidException(MethodArgumentNotValidException e) {BindingResult bindingResult = e.getBindingResult();List<String> msgList = new ArrayList<>();//将错误信息放在msgListbindingResult.getFieldErrors().stream().forEach(item->msgList.add(item.getDefaultMessage()));//拼接错误信息String msg = StringUtils.join(msgList, ",");log.error("【系统异常】{}",msg);return new RestErrorResponse(msg);
}
经过测试可在不合法的数据参数情况下返回类似如下的异常信息:
{"errMessage": "课程名称不能为空,适用人群内容过少"
}
可见校验器生效。
有时候在同一个属性上设置一个校验规则不能满足要求,比如:订单编号由系统生成,在添加订单时要求订单编号为空,在更新 订单时要求订单编写不能为空。此时就用到了分组校验,同一个属性定义多个校验规则属于不同的分组,比如:添加订单定义@NULL规则属于insert分组,更新订单定义@NotEmpty规则属于update分组,insert和update是分组的名称,是可以修改的。
可以用class类型来表示不同的分组,定义不同的接口类型(空接口)表示不同的分组,如下:
public class ValidationGroups {public interface Insert{};public interface Update{};public interface Delete{};}
定义校验规则时指定分组:
@NotEmpty(groups = {ValidationGroups.Insert.class},message = "添加课程名称不能为空")
@NotEmpty(groups = {ValidationGroups.Update.class},message = "修改课程名称不能为空")
@ApiModelProperty(value = "课程名称", required = true)
private String name;
在Controller方法中启动校验规则指定要使用的分组名:
@ApiOperation("新增课程基础信息")
@PostMapping("/course")
public CourseBaseInfoDto createCourseBase(@RequestBody @Validated({ValidationGroups.Insert.class}) AddCourseDto addCourseDto){Long companyId = 10000L;return courseBaseInfoService.createCourseBase(companyId,addCourseDto);
}
通过上述配置则可实现不同类型的异常信息在不同类型的请求过来使用模型类的时候加以区分。
如果javax.validation.constraints包下的校验规则满足不了需求可通过手写校验代码或自定义校验规则注解。
自定义校验规则注解示例如下:
定义一个自定义注解 @OnlyLetters,用于验证字段是否只包含字母字符:
package com.gavin.validation;import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;// 指定注解的适用范围(字段级别)
@Target({ElementType.FIELD, ElementType.METHOD})
// 指定注解的生命周期(在运行时可用)
@Retention(RetentionPolicy.RUNTIME)
// 关联校验器类
@Constraint(validatedBy = OnlyLettersValidator.class)
public @interface OnlyLetters {// 错误信息String message() default "只能包含字母字符";// 用于分组校验Class<?>[] groups() default {};
实现 OnlyLettersValidator 类,编写具体的校验逻辑:
package com.gavin.validation;import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;public class OnlyLettersValidator implements ConstraintValidator<OnlyLetters, String> {// 初始化方法(可以用来获取注解的属性值)@Overridepublic void initialize(OnlyLetters constraintAnnotation) {// 不需要初始化操作}// 校验逻辑@Overridepublic boolean isValid(String value, ConstraintValidatorContext context) {// 如果字段为空,不进行验证(可以在其他注解中处理空值校验)if (value == null) {return true;}// 校验字符串是否只包含字母return value.matches("[a-zA-Z]+");}
}
通过上述自定义注解,即可跟前序流程一样进行使用。
相关文章:

【实战案例】JSR303统一校验与SpringBoot项目的整合
前后端分离项目中,当前前端请求后端接口的时候通常需要传输参数,对于参数的校验应该在哪一步进行校验?Controller中还是Service中?答案是都需要校验,只不过负责的板块不一样,Controller中通常校验请求参数的…...

忘记了系统root密码,如何重置root密码?
重置root密码(CentOS7) 文章目录 重置root密码(CentOS7)[toc] 1.开启系统时,在引导界面按下字母e。 2.进入到内核界面,找到Linux开头字样一行,然后在最末尾输入参数rd.break,然后按住…...

7-基于国产化FT-M6678+JFM7K325T的6U CPCI信号处理卡
一、板卡概述 本板卡系我公司自主研发,基于6U CPCI的通用高性能信号处理平台。板卡采用一片国产8核DSP FT-C6678和一片国产FPGA JFM7K325T-2FFG900作为主处理器。为您提供了丰富的运算资源。如下图所示: 二、设计参考标准 ● PCIMG 2.0 R3.0 CompactP…...

计算机毕业设计 | SSM超市进销存管理系统(附源码)
1,绪论 1.1 开发背景 世界上第一个购物中心诞生于美国纽约,外国人迈克尔库伦开设了第一家合作商店,为了更好地吸引大量客流量,迈克尔库伦精心设计了低价策略,通过大量进货把商品价格压低,通过商店一次性集…...

手撕数据结构 —— 堆(C语言讲解)
目录 1.堆的认识 什么是堆 堆的性质 2.堆的存储 3.堆的实现 Heap.h中接口总览 具体实现 堆结构的定义 初始化堆 销毁堆 堆的插入 堆的向上调整算法 堆的插入的实现 堆的删除 堆的向下调整算法 堆的删除的实现 使用数组初始化堆 获取堆顶元素 获取堆中的数据…...
TS和JS中,string与String的区别
1. string string 是 TypeScript 的基本类型,用于表示简单的字符串值,同时它是一个原始类型,可直接表示文本数据。 2. String String 是 JavaScript 中的一个全局对象(类),用于创建字符串对象࿰…...
jna调用c++动态库linux测试
1、 编译代码和运行指令 javac -cp .:jna-5.7.0.jar:jna-platform-5.7.0.jar JnaTest.java VideoAiLibrary.java java -cp .:jna-5.7.0.jar:jna-platform-5.7.0.jar JnaTest javac -cp .:jna-5.7.0.jar:jna-platform-5.7.0.jar JnaTest.java VideoAiLibrary.java -cp 指定c…...

智诊小助手TF卡记录文件导出
若想将TF卡中记录的数据文件导出可按以下的流程进行配置: 点击主界面中的导出选项即可进入到下图中TF卡应用界面点击TF卡应用界面中“查看记录文件”的选项,进入导出文件界面。点击“选择”进入勾选文件的界面 点击“导出”后,点击“确定”即…...
Jetpack-ViewModel+LiveData+DataBinding
1.ViewModel 解决问题: 瞬态数据丢失异步调用内存泄漏类膨胀提高维护难度和测试难度 作用: 介于View视图和Model数据模型之间桥梁使视图和数据能够分离,也能保持通信 public class MainActivity extends AppCompatActivity {private Tex…...

Servlet[springmvc]的Servlet.init()引发异常
报错: 原因之一: web.xml配置文件中监听器导入依赖项错误...
总结:SQL查询变慢,常见原因分析!
文章目录 引言SQL查询慢原因索引失效特殊情况-执行计划中,key有值,还是很慢怎么办? 多表JOIN为什么互联网公司都不建议使用多表join? 索引基数太小不合理查询字段太多表中数据量太大数据库连接数不够为什么乐观锁还会导致大量的锁…...

基于webrtc实现音视频通信
与传统通信方式不同,p2p通信的实现过程不依赖于中间服务器的信息收发,直接通过信令等完成通信过程的建立; 通过websocket实现信令服务器的建立,而通过信令来确定通信双方; webrtc通过 sdp协议来完善通信双方间协议的…...

【多版本并发控制(MVCC)】
并发事务问题: MySQL隔离级别-未提交读,提交读,可重复读,序列化 隔离级别对于并发事务的解决情况 隔离级别脏读不可重复读幻读未提交读不可不可不可读已提交可不可不可可重复读 (默认)可可不可串行化&…...
常见漏洞及webshell工具的流量特征
常见攻击的流量特征 信息泄露 请求/路径中,包含 特殊文件 或 路径;响应包中,包含敏感信息(如,数据结构,用户信息,网络结构等) 弱口令爆破 非常规流量:短时间内大量数据…...

python学习-怎么在Pycharm写代码
打开Pycharm,点击文件-新建项目 2.选择pure python-点击箭头 展开 3.选择 Existing interpreter 如果 Existing interpreter 下没有相关环境 (1)点击**…** (2)选择python的安装路径 4.可修改文件名称-点击创建 …...

牛客周赛63(C++实现)
🌈个人主页:Yui_ 🌈Linux专栏:Linux 🌈C语言笔记专栏:C语言笔记 🌈数据结构专栏:数据结构 🌈C专栏:C 文章目录 1.小红的好数1.1 题目描述1.2 思路1.3 代码 2.…...

高级英语1第四版教材全解pdf课后答案+课文翻译张汉熙
《高级英语1》是张汉熙教授编著的一本英语教材,广泛用于国内高校英语专业高年级学生的教学。这本书以提高学生的英语综合能力为目标,注重语言知识的系统性和实用性,同时强调跨文化交际能力的培养。书中选材丰富,涵盖了文学、历史、…...

视频去水印软件3款推荐:好用的去水印软件分享!
在处理视频素材时,水印往往是一个令人头疼的问题。幸运的是,市面上有许多优秀的视频编辑软件能够帮助我们快速、有效地去除水印。今天,我将为大家推荐三款功能强大的视频去水印软件:影忆、Final Cut Pro X以及Adobe Premiere Pro&…...
perl文件测试操作符及其意义
perl文件测试操作符及其意义 文件测试操作符意义-r文件或目录,对目前(有效的)用户或组来说是可读的-w文件或目录,对目前(有效的)用户或组来说是可写的-x文件或目录,对目前(有效的&a…...

NC 单据模板自定义项 设置参照(自定义参照)
NC 单据模板自定义项 设置参照(自定义参照) 如图下图,NC 单据模板自定义项 设置参照: 1、选择需要设置参照的自定义字段,选择高级属性页签,在类型设置中,数据类型选择参照信息,即bd…...

深入剖析AI大模型:大模型时代的 Prompt 工程全解析
今天聊的内容,我认为是AI开发里面非常重要的内容。它在AI开发里无处不在,当你对 AI 助手说 "用李白的风格写一首关于人工智能的诗",或者让翻译模型 "将这段合同翻译成商务日语" 时,输入的这句话就是 Prompt。…...
CVPR 2025 MIMO: 支持视觉指代和像素grounding 的医学视觉语言模型
CVPR 2025 | MIMO:支持视觉指代和像素对齐的医学视觉语言模型 论文信息 标题:MIMO: A medical vision language model with visual referring multimodal input and pixel grounding multimodal output作者:Yanyuan Chen, Dexuan Xu, Yu Hu…...
进程地址空间(比特课总结)
一、进程地址空间 1. 环境变量 1 )⽤户级环境变量与系统级环境变量 全局属性:环境变量具有全局属性,会被⼦进程继承。例如当bash启动⼦进程时,环 境变量会⾃动传递给⼦进程。 本地变量限制:本地变量只在当前进程(ba…...

Swift 协议扩展精进之路:解决 CoreData 托管实体子类的类型不匹配问题(下)
概述 在 Swift 开发语言中,各位秃头小码农们可以充分利用语法本身所带来的便利去劈荆斩棘。我们还可以恣意利用泛型、协议关联类型和协议扩展来进一步简化和优化我们复杂的代码需求。 不过,在涉及到多个子类派生于基类进行多态模拟的场景下,…...
论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一)
宇树机器人多姿态起立控制强化学习框架论文解析 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一) 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化…...

微信小程序云开发平台MySQL的连接方式
注:微信小程序云开发平台指的是腾讯云开发 先给结论:微信小程序云开发平台的MySQL,无法通过获取数据库连接信息的方式进行连接,连接只能通过云开发的SDK连接,具体要参考官方文档: 为什么? 因为…...

安宝特方案丨船舶智造的“AR+AI+作业标准化管理解决方案”(装配)
船舶制造装配管理现状:装配工作依赖人工经验,装配工人凭借长期实践积累的操作技巧完成零部件组装。企业通常制定了装配作业指导书,但在实际执行中,工人对指导书的理解和遵循程度参差不齐。 船舶装配过程中的挑战与需求 挑战 (1…...
Web中间件--tomcat学习
Web中间件–tomcat Java虚拟机详解 什么是JAVA虚拟机 Java虚拟机是一个抽象的计算机,它可以执行Java字节码。Java虚拟机是Java平台的一部分,Java平台由Java语言、Java API和Java虚拟机组成。Java虚拟机的主要作用是将Java字节码转换为机器代码&#x…...
MinIO Docker 部署:仅开放一个端口
MinIO Docker 部署:仅开放一个端口 在实际的服务器部署中,出于安全和管理的考虑,我们可能只能开放一个端口。MinIO 是一个高性能的对象存储服务,支持 Docker 部署,但默认情况下它需要两个端口:一个是 API 端口(用于存储和访问数据),另一个是控制台端口(用于管理界面…...

MyBatis中关于缓存的理解
MyBatis缓存 MyBatis系统当中默认定义两级缓存:一级缓存、二级缓存 默认情况下,只有一级缓存开启(sqlSession级别的缓存)二级缓存需要手动开启配置,需要局域namespace级别的缓存 一级缓存(本地缓存&#…...