前言
后台数据的校验也是开发中比较注重的一点,用来校验数据的正确性,以免一些非法的数据破坏系统,或者进入数据库,造成数据污染,由于数据检验可能应用到很多层面,所以系统对数据校验要求比较严格且追求可变性及效率。
了解
了解一点概念性的东东。
- JSR 303 是 Java 为 Bean 数据合法性校验提供的标准框架,它已经包含在 JavaEE 6.0 中 。
- Hibernate Validator 是 JSR 303 的一个参考实现,所以它多实现了几个校验规则。
- Spring 4.0 拥有自己独立的数据校验框架,同时支持 JSR303 标准的校验框架。
- 在已经标注了 JSR303 注解的表单/命令对象前标注一个@Valid,Spring MVC 框架在将请求参数绑定到该入参对象后,就会调用校验框架根据注解声明的校验规则实施校验
- Spring MVC 是通过对处理方法签名的规约来保存校验结果的:前一个表单/命令对象的校验结果保存到随后的入参中,这个保存校验结果的入参必须是 BindingResult 或Errors 类型,这两个类都位于org.springframework.validation 包中。
- 需校验的 Bean 对象和其绑定结果对象或错误对象时成对出现的,它们之间不允许声明其他的入参
- Errors 接口提供了获取错误信息的方法,如 getErrorCount() 或getFieldErrors(String field)
- BindingResult 扩展了 Errors 接口。
支持的注解
JSR
提供的校验注解:
1 | @Null 被的注解元素必须为 null |
Hibernate Validator
提供的校验注解:
1 | @NotBlank(message =) 验证字符串非null,且长度必须大于0 |
可以在需要验证的属性上,使用多个验证方式,它们同时生效。spring boot web
已经有 hibernate-validation
的依赖,所以不需要再手动添加依赖。
使用
首先我在我的实体类上写了几个校验注解。
1 | public class SysUserEntity implements Serializable { |
使用@Validated进行校验
首先了解下:
关于@Valid和@Validated的区别联系
@Valid
:javax.validation
, 是javax,也是就是jsr303中定义的规范注解@Validated
:org.springframework.validation.annotation
, 是spring自己封装的注解。参数校验失败抛出org.springframework.validation.BindException
异常。
@Validated
是 @Valid
的一个变种,扩展了 @Valid
的功能,支持 group分组校验
的写法,所以为了校验统一,尽量使用 @Validated
在controller自定义一个接口
1 | "/valid") ( |
需要注意的有几点:
- 需要校验对象的时候,需要加上 spring 的校验注解
@Validated
,表示我们需要 spring 对它进行校验,而校验的信息会存放到其后的BindingResult中。 - BindingResult 必须和检验对象紧邻,中间不能穿插任何参数,如果有多个校验对象
@Validated @RequestBody SysUserEntity user, BindingResult result, @Validated @RequestBody SysUserEntity user1, BindingResult result1
。
我在前端用 Swagger 进行测试下。
我发送一个 body,将 手机号输错:
1 | { |
后端调试下 BindingResult 的结果,发现结果:
只要注意下 errors
属性,它是校验所有不符合规则的,是一个数组。
分组校验
有时候 ,我们在新增和更新的时候校验效果是不一样的。例如上面,我在User新增的时候需要判断密码是不是为空,但是更新的时候我不做校验。这个时候就也要用到分组校验了。
1 | "密码不能为空", groups = {AddGroup.class}) (message = |
将Contoller中的校验修改下。
1 | ( ({AddGroup.class}) SysUserEntity user, BindingResult result) |
上面的意思是只有分组是AddGroup的校验才生效,其余的校验忽略。
经过我测试,把分组情况分下:
- 在controller校验没加分组的时候,只对实体类的没有分组的注解有效。
- 在controller校验加分组的时候,只对实体类的当前分组的注解有效,没有注解的也无效。
- 当校验有两个分组的时候
@Validated({AddGroup.class, UpdateGroup.class})
,满足当前两个分组其中任意一个都可以校验,两个注解同时一起出现,也没问题,而且检验不通过的信息不会重复。
自定义校验
有时候系统提供给我们的校验注解,并不够用,我们可以自定义校验,来满足我们的业务需求。
例如:现在我们有一个需求,需要检测一条信息的敏感词汇,如sb
……文明人,举个栗子 ……
自定义校验注解
1 | // 注解可以用在哪些地方 |
规则校验实现类
1 | // 可以指定检验类型,这里选择的是 String |
所有的验证者都需要实现ConstraintValidator接口,它的接口也很形象,包含一个初始化事件方法,和一个判断是否合法的方法。
测试一下喂
现在我的用户类上,也没什么多余的字段拿出来测试,暂时把 password 字段拿来测试吧。
1 | //@NotBlank(message = "密码不能为空", groups = AddGroup.class) |
手动校验
这个是我最终想要的处理方式。
由于现在都是前后端分离开发的,校验失败的时候,抛出自定义的异常,然后统一处理这些异常,最后将相关的错误提示信息返回给前端处理。
新建一个验证工具类
1 | public class ValidatorUtils { |
它主要做的事情就是验证我们的待验证对象,验证不同通过的时候,抛出自定义异常,在后台统一处理异常就可以了。
在业务中直接调用就可以了,有分组添加分组就行:
1 | "/valid1") ( |
最后测试一下,查看返回结果是否符合预期:
手动校验的补充
决定还是采用注解的形式进行编码,本来想用处理方法参数的装配进行检验,写好了发现和 @responseBody
不能同时使用,然后发现还是可以使用 @Validated
直接校验,抛出异常, 进行捕捉异常统一处理。
1 | () |
在全局异常处理里面加上 处理绑定参数异常 org.springframework.validation.BindException
:
1 | /** |