SpringBoot实战电商项目mall(30k+star)地址:github.com/macrozheng/…
摘要
平时在开发接口的时候,常常会需要对参数进行校验,这里提供两种处理校验逻辑的方式。一种是使用Hibernate Validator来处理,另一种是使用全局异常来处理,下面我们讲下这两种方式的用法。
Hibernate Validator
Hibernate Validator是SpringBoot内置的校验框架,只要集成了SpringBoot就自动集成了它,我们可以通过在对象上面使用它提供的注解来完成参数校验。
常用注解
我们先来了解下常用的注解,对Hibernate Validator所提供的校验功能有个印象。
- @Null:被注释的属性必须为null;
- @NotNull:被注释的属性不能为null;
- @AssertTrue:被注释的属性必须为true;
- @AssertFalse:被注释的属性必须为false;
- @Min:被注释的属性必须大于等于其value值;
- @Max:被注释的属性必须小于等于其value值;
- @Size:被注释的属性必须在其min和max值之间;
- @Pattern:被注释的属性必须符合其regexp所定义的正则表达式;
- @NotBlank:被注释的字符串不能为空字符串;
- @NotEmpty:被注释的属性不能为空;
- @Email:被注释的属性必须符合邮箱格式。
使用方式
接下来我们以添加品牌接口的参数校验为例来讲解下Hibernate Validator的使用方法,其中涉及到一些AOP的知识,不了解的朋友可以参考下《SpringBoot应用中使用AOP记录接口访问日志》。
- 首先我们需要在添加品牌接口的参数
PmsBrandParam
中添加校验注解,用于确定属性的校验规则及校验失败后需要返回的信息;
1 | 复制代码/** |
- 然后在添加品牌的接口中添加@Validated注解,并注入一个BindingResult参数;
1 | 复制代码/** |
- 然后在整个Controller层创建一个切面,在其环绕通知中获取到注入的BindingResult对象,通过hasErrors方法判断校验是否通过,如果有错误信息直接返回错误信息,验证通过则放行;
1 | 复制代码/** |
- 此时我们访问添加品牌的接口,不传入
name
字段,就会返回名称不能为空
的错误信息;
自定义注解
有时候框架提供的校验注解并不能满足我们的需要,此时我们就需要自定义校验注解。比如还是上面的添加品牌,此时有个参数
showStatus
,我们希望它只能是0或者1,不能是其他数字,此时可以使用自定义注解来实现该功能。
- 首先自定义一个校验注解类FlagValidator,然后添加@Constraint注解,使用它的validatedBy属性指定校验逻辑的具体实现类;
1 | 复制代码/** |
- 然后创建FlagValidatorClass作为校验逻辑的具体实现类,实现ConstraintValidator接口,这里需要指定两个泛型参数,第一个需要指定为你自定义的校验注解类,第二个指定为你要校验属性的类型,isValid方法中就是具体的校验逻辑。
1 | 复制代码/** |
- 接下来我们就可以在传参对象中使用该注解了;
1 | 复制代码/** |
- 最后我们测试下该注解,调用接口是传入
showStatus=3
,会返回显示状态不正确
的错误信息。
优缺点
这种方式的优点是可以使用注解来实现参数校验,不需要一些重复的校验逻辑,但是也有一些缺点,比如需要在Controller的方法中额外注入一个BindingResult对象,只支持一些简单的校验,涉及到要查询数据库的校验就无法满足了。
全局异常处理
使用全局异常处理来处理校验逻辑的思路很简单,首先我们需要通过@ControllerAdvice注解定义一个全局异常的处理类,然后自定义一个校验异常,当我们在Controller中校验失败时,直接抛出该异常,这样就可以达到校验失败返回错误信息的目的了。
使用到的注解
@ControllerAdvice:类似于@Component注解,可以指定一个组件,这个组件主要用于增强@Controller注解修饰的类的功能,比如说进行全局异常处理。
@ExceptionHandler:用来修饰全局异常处理的方法,可以指定异常的类型。
使用方式
- 首先我们需要自定义一个异常类
ApiException
,当我们校验失败时抛出该异常:
1 | 复制代码/** |
- 然后创建一个断言处理类
Asserts
,用于抛出各种ApiException
;
1 | 复制代码/** |
- 然后再创建我们的全局异常处理类
GlobalExceptionHandler
,用于处理全局异常,并返回封装好的CommonResult对象;
1 | 复制代码/** |
- 这里拿用户领取优惠券的代码为例,我们先对比下改进前后的代码,首先看Controller层代码。改进后只要Service中的方法执行成功就表示领取优惠券成功,因为领取不成功的话会直接抛出ApiException从而返回错误信息;
1 | 复制代码/** |
- 再看下Service接口中的代码,区别在于返回结果,改进后返回的是void。其实CommonResult的作用本来就是为了把Service中获取到的数据封装成统一返回结果,改进前的做法违背了这个原则,改进后的做法解决了这个问题;
1 | 复制代码/** |
- 再看下Service实现类中的代码,可以看到原先校验逻辑中返回CommonResult的逻辑都改成了调用Asserts的fail方法来实现;
1 | 复制代码/** |
- 这里我们输入一个没有的优惠券ID来测试下该功能,会返回
优惠券不存在
的错误信息。
优缺点
使用全局异常来处理校验逻辑的优点是比较灵活,可以处理复杂的校验逻辑。缺点是我们需要重复编写校验代码,不像使用Hibernate Validator那样只要使用注解就可以了。不过我们可以在上面的Asserts
类中添加一些工具方法来增强它的功能,比如判断是否为空和判断长度等都可以自己实现。
总结
我们可以两种方法一起结合使用,比如简单的参数校验使用Hibernate Validator来实现,而一些涉及到数据库操作的复杂校验使用全局异常处理的方式来实现。
项目源码地址
公众号
mall项目全套学习教程连载中,关注公众号第一时间获取。
本文转载自: 掘金