数据验证的必要性
对于任何一个应用而言,客户端做的数据有效性验证都不是安全有效的,而数据验证又是一个企业级项目架构上最为基础的功能模块,这时候就要求我们在服务端接收到数据的时候也对数据的有效性进行验证。比如字段非空,字段长度限制,邮箱格式验证等等,写这些与业务逻辑关系不大的代码个人感觉又比较麻烦:
验证代码繁琐,重复劳动
方法内代码显得冗长
每次要看哪些参数验证是否完整,需要去翻阅验证逻辑代码
hibernate validator 提供了一套比较完善、便捷的验证实现方式。
spring-boot-starter-web包里面有hibernate-validator包,不需要引用hibernate validator依赖。
validator校验demo
先来看一个简单的demo,添加了Validator的注解
1 2 3 |
|
在需要验证的model属性上添加验证注解
|
要在做验证的Service接口上添加注解@Validated,在需要验证的接口方法实体参数前添加注解@Validated,代码如下:
|
以上是对实体属性的验证,当然也可以直接对接口方法的参数添加注解,示例如下:
|
验证标签含义
参数验证非常方便,字段上注解+验证不通过提示信息即可代替手写一大堆的非空和字段限制验证代码。当然除上述看到的验证注解,还有很多,我做的简单整理,如有遗漏,欢迎大家补充:
限制 | 说明 |
---|---|
@Null | 限制只能为null |
@NotNull | 限制必须不为null |
@AssertFalse | 限制必须为false |
@AssertTrue | 限制必须为true |
@DecimalMax(value) | 限制必须为一个不大于指定值的数字 |
@DecimalMin(value) | 限制必须为一个不小于指定值的数字 |
@Digits(integer,fraction) | 限制必须为一个小数,且整数部分的位数不能超过integer,小数部分的位数不能超过fraction |
@Future | 限制必须是一个将来的日期 |
@Max(value) | 限制必须为一个不大于指定值的数字 |
@Min(value) | 限制必须为一个不小于指定值的数字 |
@Past | 限制必须是一个过去的日期 |
@Pattern(value) | 限制必须符合指定的正则表达式 |
@Size(max,min) | 限制字符长度必须在min到max之间 |
@Past | 验证注解的元素值(日期类型)比当前时间早 |
@NotEmpty | 验证注解的元素值不为null且不为空(字符串长度不为0、集合大小不为0) |
@NotBlank | 验证注解的元素值不为空(不为null、去除首位空格后长度为0),不同于@NotEmpty,@NotBlank只应用于字符串且在比较时会去除字符串的空格 |
验证注解的元素值是Email,也可以通过正则表达式和flag指定自定义的email格式 |
数据验证进阶
以上是对数据验证的简单介绍,接下来咱们深入了解下参数校验的玩法。
个性化验证
需要依赖实体的其中几个属性去做判断验证。(如:添加供应商时,选择供应商属性"结算账期"为"授信账期"时,那么"授信账期天数"、"授信额度是否限制"、"授信额度"三个属性必填;反之,这三个属性不需验证是否填写),示例代码如下:
|
对上述代码做下简单说明:@ScriptAssert是脚本验证,script属性要填写你自己的验证方法,此方法必须是public static 修饰,message是验证未通过时的错误信息。对checkPaymentDaysType验证方法做下说明,验证通过时返回true,验证未通过返回false。
重复验证
相信大家肯定经常遇到一些重复性的验证,这是需要去查询数据库,并不能简单验证实体属性,以下即是对这种情况的示例,也是通过@ScriptAssert脚本注解实现,在需要验证的实体写法如下:
|
验证名称重复的组件
|
借助于验证组件完成名称重复的验证。
对象级联校验
以上验证都是对实体单个属性,如果实体属性是个自定义对象,同样需要对属性对象做验证时,怎么实现级联验证呢?很简单,在需要验证的属性上添加@Valid注解即可:
|
这样就能实现对象级联验证,不仅对实体本身属性做验证,也会对复杂对象属性进行验证。
分组校验
经过以上介绍,大家已经能基本使用validator进行验证。但如果针对同一个实体的同一个属性,方法A需要验证不能为空;方法B不需要进行验证,这种情况,如果不再新建一个实体的情况下,怎么来解决呢,下面我们所说的分组即可解决此场景问题。
举例:供应商实体,在添加时,id属性可以为空;但是在修改时,id属性不能为空,针对这种情况,做下代码实现:
验证实体
|
接下来在Service中的代码如下:
分组验证Service
|
对以上代码做下解释:首先我们能在实体看到验证注解中多了一个groups属性,它的值是接口的集合(必须是接口),这样就定义了分组,同样可以定义多个分组(如:{ Create.class, Update.class })或者没有分组;那么Service方法在做验证的时候就需要去添加分组的属性。此处对分组验证范围说下:
如果Service验证注解添加了分组,那么就只会对实体上有该分组的属性;
如果Service验证注解没有分组,那么就只对实体上没有分组设置的属性进行验证;
针对以上示例,我做了个表格,有助于理解:
Service方法 | 验证注解 | 验证实体属性 |
---|---|---|
create | @Validated({ Create.class, Default.class}) | SupplierBiz.branchId (groups = Create.class) SupplierBiz.type (groups = { Create.class, Update.class }) SupplierBiz.supplierId (groups 无) SupplierBiz.contractStartTime (groups 无) |
update | @Validated({ Update.class, Create.class }) | SupplierBiz.id (groups = Update.class) SupplierBiz.branchId (groups = Create.class) SupplierBiz.type (groups = { Create.class, Update.class }) |
此处注意:没有设置groups分组的,默认即为Default分组。
到此Validator就基本介绍完毕,希望对各位在开发时有所帮助。
注:集合校验需要加@Valid注解
扩展
Spring Validator已经提供了比较完善的验证注解,但有些场景可能还是不能够满足,所以可以自定义编写验证注解,以下是一个自定义注解,代码片段如下:
|
|
使用方法如下:
|
@NotRepeatable 注解的value为选填,针对复杂对象,填写了若干属性,会针对填写的属性进行重复验证;如果不填写,则调用对象的toString方法进行重复验证。
如上代码:会验证id和name,如果集合中不同的元素存在id和name都一样的情况,则任务元素重复;如果不填写value,则会调用SupplierBizContact.toString()方法验重。
|
如果是基础数据类型的List集合,不需要设置value,设置value也会忽略。