SpringBoot参数校验@Validated、@Valid

SpringBoot参数校验@Validated、@Valid(javax.validation)

一、应用场景

在实际开发中,前端校验并不安全,任何人都可以通过接口来调用我们的服务,就算加了一层token的校验,有心人总会转空子,来传各式各样错误的参数,如果后端不校验,导致数据库数据混乱、特别是关于金额的数据,可能一个接口把公司都给干倒了

二、原生参数校验

0、返回类(可以不用看)

/*** 用于返回* @param <T>*/
@ApiModel("统一返回类")
public class Results<T> {public static final String ERROR = "500";public static final String SUCCESS = "200";/*** 返回码*/@ApiModelProperty("返回码,正确码为:200")private String resCode ;/*** 返回消息*/@ApiModelProperty("返回消息")private String msg ;/*** 返回实体*/@ApiModelProperty("返回实体")private T obj;public static <T> Results<T> success(){return success(SUCCESS,"成功",null);}public static <T> Results<T> success(String msg){return success(SUCCESS,msg,null);}public static <T> Results<T> success(T obj){return success(SUCCESS,"成功",obj);}public static <T> Results<T> success(String msg,T obj){return success(SUCCESS,msg,obj);}public static <T> Results<T> success(String resCode,String msg,T obj){Results<T> result = new Results<T>();result.setResCode(resCode);result.setMsg(msg);result.setObj(obj);return result;}public static <T> Results<T> failed() {return failed(ERROR,"失败",null);}public static <T> Results<T> failed(String msg) {return failed(ERROR,msg,null);}public static <T> Results<T> failed(String msg,T obj) {return failed(ERROR,msg,obj);}public static <T> Results<T> failed(String resCode,String msg) {return failed(resCode,msg,null);}public static <T> Results<T> failed(Integer resCode,String msg) {return failed(String.valueOf(resCode),msg);}public static <T> Results<T> failed(String resCode,String msg,T obj) {Results<T> result = new Results<T>();result.setResCode(resCode);result.setMsg(msg);result.setObj(obj);return result;}public static <T> Results<T> failedNoPermission() {return failed(90005,"没有权限");}public static <T> Results<T> failedNoPermission(String msg) {return failed(90005,msg);}public static <T> Results<T> failedParameterException() {return failed(90004,"参数异常");}public static <T> Results<T> failedParameterException(String msg) {return failed(90004,msg);}public static <T> Results<T> failedLoginException() {return failed(90002,"登录失败");}public static <T> Results<T> failedLoginException(String msg) {return failed(90002,msg);}public String getResCode() {return resCode;}public void setResCode(String resCode) {this.resCode = resCode;}public String getMsg() {return msg;}public void setMsg(String msg) {this.msg = msg;}public T getObj() {return obj;}public void setObj(T obj) {this.obj = obj;}@Overridepublic String toString() {return "Results{" +"resCode='" + resCode + '\'' +", msg='" + msg + '\'' +", obj=" + obj +'}';}
}

1、实体类

@ApiModel("测试 validation 入参")
@Data
public class TestDto {@ApiModelProperty(value = "名字",required = true)private String name;@ApiModelProperty(value = "年龄",required = true)private Integer age;@ApiModelProperty(value = "爱好",required = true)private List<String> hobbies;
}

2、服务层(为了方便,我直接跟Controller写在一起了)

我们可以看见如果参数过大,要一个一个筛选条件十分浪费时间

@RestController
// lombok 的日志注解
@Slf4j
// swagger 的注解
@Api("测试")
public class TestController {@PostMapping("/testValidation")// swagger 的注解@ApiOperation("测试 validation")public Results testValidation(@RequestBody TestDto dto){try {log.info("test 入参 dto={}",dto);// 这要一个一个的塞,很浪费时间if (dto.getName() == null || "".equals(dto.getName().trim())){return Results.failed("名字不能为空");}if (dto.getAge() == null){return Results.failed("年龄不能为空");}if (dto.getHobbies() == null || dto.getHobbies().size() == 0){return Results.failed("爱好不能为空");}return Results.success();} catch (Exception e) {log.error("test 报错",e);return Results.failed();}}
}

三、使用 javax.validation 进行参数校验

1、导包

        <dependency><groupId>javax.validation</groupId><artifactId>validation-api</artifactId><version>2.0.1.Final</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId></dependency>

2、全局异常处理类

@RestControllerAdvice
public class ExceptionControllerAdvice {@ResponseStatus(HttpStatus.OK)@ExceptionHandler(MethodArgumentNotValidException.class)public Results MethodArgumentNotValidExceptionHandler(MethodArgumentNotValidException e) {// 从异常对象中拿到ObjectError对象BindingResult br = e.getBindingResult();if (br.hasFieldErrors()) {List<FieldError> fieldErrorList = br.getFieldErrors();List<String> errors = new ArrayList<>(fieldErrorList.size());for (FieldError error : fieldErrorList) {errors.add(error.getField() + ":" + error.getDefaultMessage());}// 然后提取错误提示信息进行返回return Results.failed(errors.toString());}// 然后提取错误提示信息进行返回return Results.failed("校验错误");}}
如果不加这个全局处理类,只会给前端返回这样的参数

在这里插入图片描述

加上全局配置类

在这里插入图片描述

3、实体类

@ApiModel("测试 validation 入参")
@Data
public class TestDto {@ApiModelProperty(value = "名字",required = true)// 适用于 String 类型的数据上,加了@NotBlank 注解的参数不能为 Null 且 trim() 之后 size > 0,必须有实际字符@NotBlank(message = "名字不能为空")private String name;@ApiModelProperty(value = "年龄",required = true)@NotNull(message = "年龄不能为空")// 适用于基本数据类型(Integer,Long,Double等等),当 @NotNull 注解被使用在 String 类型的数据上,则表示该数据不能为 Null(但是可以为 Empty)private Integer age;@ApiModelProperty(value = "爱好",required = true)// 适用于 String、Collection集合、Map、数组等等,加了@NotEmpty 注解的参数不能为 Null 或者 长度为 0@NotEmpty(message = "年龄不能为空")private List<String> hobbies;
}

4、服务层(为了方便,我直接跟Controller写在一起了)

必须要加上 @Valid 或者 @Validated,后续我会讲解这两个有什么不同,目前来说,都可以用,但推荐用 @Validated

@RestController
@Slf4j
@Api("测试")
public class TestController {@PostMapping("/testValidation")@ApiOperation("测试 validation")// 必须要加上 @Valid 或者 @Validatedpublic Results testValidation(// 必须要加上 @Valid 或者 @Validated@Valid @RequestBody TestDto dto){try {log.info("test 入参 dto={}",dto);return Results.success();} catch (Exception e) {log.error("test 报错",e);return Results.failed();}}
}

6、测试

在这里插入图片描述

四、javax.validation 包下其它常用的校验注解:

这个颜色的是常用的

注解含义
@Null任何类型 必须为null
@NotBlank字符串、字符 字符类不能为null,且去掉空格之后长度大于
@NotNull任何类型 不能为null
@Length(min = 6, max = 8, message = “密码长度为6-8位。”)字符串的大小必须在指定的范围内
@NotEmpty适用于 String、Collection集合、Map、数组等等,加了@NotEmpty 注解的参数不能为 Null 或者 长度为 0
@AssertTrueBoolean、boolean 布尔属性必须是true
@AssertFalseBoolean、boolean 布尔属性必须是false
@Min(10)必须是一个数字,其值必须大于等于指定的最小值(我这填的是10)(整型)
@Max(10)必须是一个数字,其值必须小于等于指定的最大值(我这填的是10)(整型)
@DecimalMin(“10”)必须是一个数字,其值必须大于等于指定的最小值(我这填的是10)(字符串,可以是小数)
@DecimalMax(“10”)必须是一个数字,其值必须小于等于指定的最大值(我这填的是10)(字符串,可以是小数)
@Size(max = 10,min = 1)集合 限定集合大小
@Digits(integer = 3, fraction = 2, message = “请输入有效的数字”)
private double number;
@Digits 用于验证数字的整数位数和小数位数。该注解的 integer 和 fraction 属性分别用于指定整数位数和小数位数的限制。
integer 属性用于指定数字的最大整数位数。它是一个整数值,表示数字允许的最大整数位数。例如,integer = 3 表示数字最多可以有三位整数部分。
fraction 属性用于指定数字的最大小数位数。它是一个整数值,表示数字允许的最大小数位数。例如,fraction = 2 表示数字最多可以有两位小数部分。
@Past时间、日期 必须是一个过去的时间或日期
@Future时期、时间 必须是一个未来的时间或日期
@Email字符串 必须是一个邮箱格式
@Pattern(regexp = “[a-zA-Z]*”, message = “密码不合法”)字符串、字符 正则匹配字符串
@Range(max = 150, min = 1, message = “年龄范围应该在1-150内。”)数字类型(原子和包装) 限定数字范围(长整型)
@URL(protocol=,host=, port=,regexp=, flags=)被注释的字符串必须是一个有效的url
@CreditCardNumber被注释的字符串必须通过Luhn校验算法,银行卡,信用卡等号码一般都用Luhn计算合法性
@ScriptAssert(lang=, script=, alias=)要有Java Scripting API 即JSR 223 (“Scripting for the JavaTM Platform”)的实现
@SafeHtml(whitelistType=, additionalTags=)classpath中要有jsoup包

五、@Validated 与 @Valid 比较

1、文字讲解

Spring Validation验证框架对参数的验证机制提供了@Validated(Spring’s JSR-303规范,是标准JSR-303的一个变种),javax提供了@Valid(标准JSR-303规范),配合BindingResult可以直接提供参数验证结果。

@Valid属于javax.validation包下,是jdk给提供的 是使用Hibernate validation的时候使用

@Validated是org.springframework.validation.annotation包下的,是spring提供的 是只用Spring Validator校验机制使用

说明:java的JSR303声明了@Valid这类接口,而Hibernate-validator对其进行了实现

@Validation对@Valid进行了二次封装,在使用上并没有区别,但在分组、注解位置、嵌套验证等功能上有所不同,这里主要就这几种情况进行说明。

在检验Controller的入参是否符合规范时,使用@Validated或者@Valid在基本验证功能上没有太多区别。但是在分组、注解地方、嵌套验证等功能上两个有所不同:

  1. 分组

@Validated:提供了一个分组功能,可以在入参验证时,根据不同的分组采用不同的验证机制,这个网上也有资料,不详述。@Valid:作为标准JSR-303规范,还没有吸收分组的功能。

  1. 注解地方

@Validated:用在类型、方法和方法参数上。但不能用于成员属性(field)

@Valid:可以用在方法、构造函数、方法参数和成员属性(field)上 所以可以用@Valid实现嵌套验证

总结:

@Valid 和 @Validated 两者都可以对数据进行校验,待校验字段上打的规则注解(@NotNull, @NotEmpty等)都可以对 @Valid 和 @Validated 生效;

@Valid 进行校验的时候,需要用 BindingResult 来做一个校验结果接收。当校验不通过的时候,如果手动不 return ,则并不会阻止程序的执行;

@Validated 进行校验的时候,当校验不通过的时候,程序会抛出400异常,阻止方法中的代码执行,这时需要再写一个全局校验异常捕获处理类,然后返回校验提示。

总体来说,@Validated 使用起来要比 @Valid 方便一些,它可以帮我们节省一定的代码,并且使得方法看上去更加的简洁。

2、代码讲解,groups属性

在开发中,新增、修改两个接口,一般关系就在于新增时ID可以为空,修改时ID不能为空,那我们如果要使用 validation 用于参数校验,创建两个实体类就非常的不划算,这时

①、创建一个update接口

import javax.validation.groups.Default;public interface Update extends Default {
}
②、修改实体类
@ApiModel("测试 validation 入参")
@Data
public class TestDto {@ApiModelProperty(value = "ID",required = true)// 新增时ID为空,修改时ID不能为空@NotNull(message = "ID不能为空",groups = Update.class)private Integer id;@ApiModelProperty(value = "名字",required = true)@NotBlank(message = "名字不能为空")private String name;
}
③、服务层(为了方便,我直接跟Controller写在一起了)
@RestController
@Slf4j
@Api("测试")
public class TestController {@PostMapping("/testAdd")@ApiOperation("测试 新增")public Results testAdd(@Validated @RequestBody TestDto dto){try {log.info("testAdd 入参 dto={}",dto);return Results.success();} catch (Exception e) {log.error("testAdd 报错",e);return Results.failed();}}@PostMapping("/testUpdate")@ApiOperation("测试 新增")public Results testUpdate(@Validated(Update.class) @RequestBody TestDto dto){try {log.info("testUpdate 入参 dto={}",dto);return Results.success();} catch (Exception e) {log.error("testUpdate 报错",e);return Results.failed();}}
}
⑤、测试
新增:

在这里插入图片描述

修改:

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Django笔记(一):环境部署

目录 Python虚拟环境 安装virtualenv 创建环境 激活环境 关闭&#xff1a; 安装Django VSCode配置 Python插件 Django插件 解释器选择 Django部署 创建项目 创建app 创建模板 编写视图 编写路由 启动服务器 访问 Python虚拟环境 安装virtualenv pip i…

gateway Redisson接口级别限流解决方案

文章目录 前言1. 计数器算法&#xff08;固定窗口限流器&#xff09;2. 滑动窗口日志限流器3. 漏桶算法&#xff08;Leaky Bucket&#xff09;4. 令牌桶算法&#xff08;Token Bucket&#xff09;5. 限流队列应用场景实现工具 一、Redisson简介二、Redisson限流器的原理三、Red…

情人节专属--html5 canvas制作情人节告白爱心动画特效

💖效果展示 💖html展示 <!doctype html> <html> <head> <meta charset=

pyqtgraph绘图类

pyqtgraph绘图类 pyqtgraph绘图有四种方法: 方法描述pyqtgraph.plot()创建一个新的QWindow用来绘制数据PlotWidget.plot()在已存在的QWidget上绘制数据PlotItem.plot()在已存在的QWidget上绘制数据GraphicsLayout.addPlot()在网格布局中添加一个绘图 上面四个方法都接收同样…

视觉检测系统:工厂生产零部件的智能检测

在工厂的生产加工过程中&#xff0c;工业视觉检测系统被广泛应用&#xff0c;并且起着重要的作用。它能够对不同的零部件进行多功能的视觉检测&#xff0c;包括尺寸和外观的缺陷。随着制造业市场竞争越来越激烈&#xff0c;对产品质检效率的要求不断提高&#xff0c;传统的人工…

2024年Web3.0数字资产六大趋势(完整版)

作者&#xff1a;于佳宁、Romeo Wang&#xff08;方军、周芳鸽、李祺虹、张睿彬亦参与本报告撰写工作) 出品方&#xff1a;Uweb & Techub News 点击查看原文链接&#xff1a;2024年Web3.0数字资产六大趋势 历史上的每次技术革命都告诉我们&#xff0c;成功的关键不仅在于…

国标GB28181安防视频监控EasyCVR级联后上级平台视频加载慢的原因排查

国标GB28181协议安防视频监控系统EasyCVR视频综合管理平台&#xff0c;采用了开放式的网络结构&#xff0c;可以提供实时远程视频监控、视频录像、录像回放与存储、告警、语音对讲、云台控制、平台级联、磁盘阵列存储、视频集中存储、云存储等丰富的视频能力&#xff0c;同时还…

jeecgboot 前端bug or 后端 看图

无法显示文本 只能显示value 很恶心 如果用 varchar 就可以 不知道有没有别的方式 用int 解决 ,可能是我没有发现好的方法

SpringBoot连接远程服务器redis

SpringBoot连接远程服务器redis 1、指定redis配置启动 进入redis安装地址&#xff0c;我这里安装的是 /usr/local/src/redis-6.2.6 先copy一份配置文件 cp redis.conf redis.conf.bck然后修改配置文件信息 vim redis.conf bind 0.0.0.0 # 守护进程&#xff0c;修改为yes后即可…

渗透测试之用Kali2022渗透Windows 10系统

环境: 1.渗透者IP:192.168.1.35 系统: KALI2022(vmware 16.0) 2.靶机IP:192.168.1.16 系统: Windows10 3.USB无线网卡 问题描述: 渗透测试之用Kali2022,测试Windows 10 解决方案: 一、准备 1.KALI虚机设置用外置无线网卡(预防反查到你本机) 2.制作脚本,社工…

【HTML】-- 01 初识HTML

HTML 1.初识HTML Hyper Text Markup Language&#xff1a;超文本标记语言 1.1 W3C标准 W3C World Wide Web Consortium(万维网联盟)成立于1994年&#xff0c;Web技术领域最权威和最具影响力的国际中立性技术标准机构http://www.w3.org/http://www.chinaw3c.org/ W3C标准包括…

【设计模式-3.3】结构型——享元模式

说明&#xff1a;说明&#xff1a;本文介绍设计模式中结构型设计模式中的&#xff0c;享元模式&#xff1b; 游戏地图 在一些闯关类的游戏&#xff0c;如超级玛丽、坦克大战里面&#xff0c;游戏的背景每一个关卡都不相同&#xff0c;但仔细观察可以发现&#xff0c;其都是用…