一文学会JSR-303 参数校验,真香
本文源自 公-众-号 IT老哥 的分享
IT老哥,一个在大厂做高级Java开发的程序员,每天分享技术干货文章
早期的参数校验形式
在早期的时候,java的参数校验停留在获取参数之后在代码层面做校验,类似如下操作:
@PostMapping("/test")
public String test(@RequestBody TestRequest request) {
if (StringUtils.isEmpty(request.getName())) {
return "姓名不能为空";
}
if (request.getName().length() > 6) {
return "姓名长度不能超过6";
}
if (StringUtils.isEmpty(request.getPhone())) {
return "电话不能为空";
}
if (!isPhone(request.getPhone())) {
return "电话号码格式不正确";
}
return "SUCCESS";
}
/**
* 校验电话格式
*/
private boolean isPhone(String phone) {
return true;
}
如代码中看到,每一种校验规则都需要在代码里面实现,那么光是参数校验
也会耗费开发人员大量的精力
,开发效率也会大幅降低
。
JSR-303简介
java6里面推出了一种规范:JSR-303
,JSR是Java Specification Requests
的缩写,意思是Java 规范提案,又叫做Bean Validation。
JSR 303是Java为bean数据合法性校验
提供的标准框架
。Hibernate Validator是Bean Validation的参考实现。
JSR-303参数校验注解
Bean Validation中内置的注解
Hibernate Validator 附加的注解
使用JSR303规范来做参数校验
我们将上面那个没使用JSR303的代码做改造
@Data
public class TestRequest {
@NotEmpty(message = "姓名不能为空")
@Length(min = 1, max = 6, message = "姓名长度必须在1-6之间")
private String name;
@NotBlank(message = "电话号码不能为空")
@Pattern(regexp = "^134[0-8]\d{7}$|^13[^4]\d{8}$|^14[5-9]\d{8}$|^15[^4]\d{8}$|^16[6]\d{8}$|^17[0-8]\d{8}$|^18[\d]{9}$|^19[8,9]\d{8}$", message = "电话号码格式不正确")
private String phone;
@Email(message = "邮件格式不正确")
private String email;
@NotNull(message = "年龄不能为空")
@Max(value = 18,message = "年龄不能超过18")
private Integer age;
}
用@Valid对controller校验
@PostMapping("/test")
public String test(@RequestBody @Valid TestRequest request) {
return "SUCCESS";
}
改造效果
@Valid与@Validated区别
首先看一下他们所属的包
:
可以看到@Validated属于spring
,而@Valid属于javax
。
- @Validated :
org.springframework.validation.annotation.Validated
- @Valid:
javax.validation.Valid
但是在实际的基本使用中,这2者是没有区别的(注意这里说的是基本使用
,也就是说,使用@Valid与@Validated是等价
的)。
@Validated对controller校验
@PostMapping("/test")
public String test(@RequestBody @Validated TestRequest request) {
return "SUCCESS";
}
将注解替换一下,也能得到相同的结果:
两个注解源码
- @Valid
@Target({ METHOD, FIELD, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
@Documented
public @interface Valid {
}
- @Validated
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Validated {
Class<?>[] value() default {};
}
通过源码可以看到@Validated可以在类上面使用
,并且多了一个value属性
。
@Validated提供了一个分组功能
,在校验参数时,可以根据不同的分组
采用不同的校验机制
。没有添加分组属性时,默认验证没有分组的验证属性。
@Validated分组校验
编写校验分组标识:
public class ValidatedGroup {
public interface Add extends Default {}
public interface Delete extends Default {}
public interface Update extends Default {}
public interface Query extends Default {}
}
测试请求类:
@Data
public class TestSaveRequest {
@NotNull(groups = {ValidatedGroup.Update.class, ValidatedGroup.Delete.class}, message = "更新或者删除时id不能为空")
private Long id;
@NotBlank(groups = {ValidatedGroup.Update.class}, message = "更新时姓名不能为空")
private String name;
@NotNull(groups = {ValidatedGroup.Add.class}, message = "新增时余额不能为空")
@Digits(integer = 10, fraction = 2)
private BigDecimal balance;
@NotBlank(groups = {ValidatedGroup.Add.class}, message = "新增时电话不能为空")
@Pattern(regexp = "^134[0-8]\d{7}$|^13[^4]\d{8}$|^14[5-9]\d{8}$|^15[^4]\d{8}$|^16[6]\d{8}$|^17[0-8]\d{8}$|^18[\d]{9}$|^19[8,9]\d{8}$", message = "电话号码格式不正确")
private String phone;
@NotBlank(groups = {ValidatedGroup.Add.class}, message = "新增时邮件不能为空")
@Email
private String email;
}
根据我们编写的测试请求类型可预期:
- 校验分组为Add时,会校验balance、phone、email三个请求字段
- 校验分组为Update时,会校验id、name字段
测试如下:
@PostMapping("/testAdd")
public String testAdd(@RequestBody @Validated(value = ValidatedGroup.Add.class) TestSaveRequest request) {
return "SUCCESS";
}
@PostMapping("/testUpdate")
public String testUpdate(@RequestBody @Validated(value = ValidatedGroup.Update.class) TestSaveRequest request) {
return "SUCCESS";
}
testAdd结果:
testUpdate结果:
结果与预期完全符合。@Validated
注解在分组校验
时候,可以节省很多额外的开发,特别是当新增
和更新
时。
一个需要传递主键一个不需要传递主键的情形,以前需要一个AddRequest、一个UpdateRequest,现在只有需要一个就够了。
嵌套校验
什么是嵌套校验呢?上代码:
@Data
public class TestNestRequest {
@NotNull(message = "id不能为空")
private Long id;
@NotNull(message = "嵌套对象请求数据不能为空")
private ItemRequest itemRequest;
}
@Data
public class ItemRequest {
@NotEmpty(message = "name不能为空")
private String name;
}
@PostMapping("/testNest")
public String testNest(@RequestBody @Valid TestNestRequest request) {
return "SUCCESS";
}
测试结果如下:
只校验了id
,没有校验嵌套
对象中的name
属性。
如果需要校验嵌套对象中的属性,需要改造一下TestNestRequest
类。
在itemRequest
属性上加上@Valid注解
,方能校验嵌套对象中的属性
改造如下:
@Data
public class TestNestRequest {
@NotNull(message = "id不能为空")
private Long id;
@Valid
@NotNull(message = "嵌套对象请求数据不能为空")
private ItemRequest itemRequest;
}
测试结果:
一定要注意, 嵌套验证必须用@Valid
注解。
云服务器,云硬盘,数据库(包括MySQL、Redis、MongoDB、SQL Server),CDN流量包,短信流量包,cos资源包,消息队列ckafka,点播资源包,实时音视频套餐,网站管家(WAF),大禹BGP高防(包含高防包及高防IP),云解析,SSL证书,手游安全MTP,移动应用安全、 云直播等等。
- Linux服务器的进程查看命令详解
- 另类SEO分享:利用JS封装iframe躲过搜索引擎的抓取
- Linux :MAC 地址克隆方法
- 解决WordPress修改数据库表前缀后无法登陆的问题
- 解决启用WP-Super-Cache后出现的几个问题
- Shell下制作自解压安装包,实现脚本的简单加密
- WordPress(Twenty Ten主题)文章副标题修改教程
- ipvsadm启动报错解决办法,另附ipvsadm详细参数
- Linux:mv 命令的10个实用例子
- Linux优化方法收集与整理
- 常用MySQL语句搜集整理
- ASM 翻译系列第十二弹:ASM Internal amdu - ASM Metadata Dump Utility
- 分享一个Linux无法创建文件夹,但是目录权限却显示正常的问题和解决
- 桌面白屏(Active故障)修复批处理
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- python调用私有属性的方法总结
- PHP+MySQL实现在线测试答题实例
- Python异常处理机制结构实例解析
- PHP字符串与数组处理函数用法小结
- 详解Flask前后端分离项目案例
- Laravel5.1 框架表单验证操作实例详解
- 通过实例了解Python异常处理机制底层实现
- header函数设置响应头解决php跨域问题实例详解
- Linux采用双网卡bond、起子接口的方式
- PHP实现字母数字混合验证码功能
- php+pdo实现的购物车类完整示例
- CentOS7怎么执行PHP定时任务详解
- Linux下PHP+Apache的26个必知的安全设置
- linux中ssh免密通信的实现
- 怎么修改CentOS服务器时间为北京时间