背景

目前司内系统的技术架构正在逐步切换到微服务(SpringCloud),而微服务相互之间是通过 Feign 进行通信的,就一个微服务来说由两个部分组成:

  • 接口,例如:annoroad-alpha-facade,这里只提供接口的定义
    在这里插入图片描述
  • 实现接口的服务,例如:annoroad-alpha,这里是对接口的实现(spring boot)
    在这里插入图片描述
    目前,对于微服务接口上参数的校验要不就是不进行任何校验,要不就是通过硬编码到代码里的校验逻辑来实现,这两种方式都不理想。 spring-boot-starter-validation 很好的解决了这些问题,通过在接口的参数上加上对应的注解(例如:@NotNull 等等),就可以很优雅的完成对参数的校验,完美~~~~

配置使用

如何在微服务(一个 spring boot 项目)中使用 spring-boot-starter-validation 呢?下面,我们就一起来看下需要进行怎么样的配置:

  1. 在负责接口定义的项目(anoroad-alpha-facade)中的 pom.xml 文件增加 spring-boot-starter-validation 依赖,如下:

     <dependencies>
     	...
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
            <version>2.1.1.RELEASE</version>
        </dependency>
        ...
    </dependencies>
    
  2. 还是在负责接口定义的项目(anoroad-alpha-facade)中,对需要进行参数校验的接口上增加相关注解,如下:

    import org.springframework.cloud.openfeign.FeignClient;
    import org.springframework.validation.annotation.Validated;
    import org.springframework.web.bind.annotation.*;
    
    import javax.validation.Valid;
    import javax.validation.constraints.Min;
    import javax.validation.constraints.NotEmpty;
    import java.util.List;
    
    @FeignClient(value = "annoroad-alpha",  url = "${annoroad.ms.annoroad-alpha.url}")
    @Validated
    public interface UserFacade {
        @PostMapping(value = "/user/hello")
        UserDto hello(@RequestParam("name") @NotEmpty String name);
    
        @PostMapping(value = "/user/detail")
        UserDto detail(@RequestParam("id") @Min(1) long id);
    
        @PostMapping("/user/add")
        UserDto add(@RequestBody @Validated UserDto userDTO);
        
        @PostMapping("/param/list")
        void testParamList(@RequestBody @Valid List<UserDto> userList);
    }
    

    在这段儿代码片段中列举出来的 4 个接口,展示了 3 种不同的使用场景

    • 对 @RequestParam 注解修饰的参数(name、id)进行校验

      对于这样的情形,要想让注解生效,除了在参数上加注解以外,例如:@NotEmpty、@Min,同时还需要在类上增加 @Validated 注解
      在这里插入图片描述
      注:这里把 @Validated 注解放在方法上或者是方法参数前都是可以的,@Validated 注解统一放在类上边省去了一个一个方法上或者入参上添加该注解,如果如下:

      在方法上加 @Validated 注解:
      在这里插入图片描述
      在方法参数前加 @Validated 注解:
      在这里插入图片描述

    • 对 @RequestBody 注解修饰的对象(UserDto)进行校验

      对于这样的情形,首选需要在接口定义那里对参数(UserDto)加上 @Validated 注解
      在这里插入图片描述
      同时还需要在 UserDto 中那些要校验的属性上增加注解,例如:@NotEmpty、@Min
      在这里插入图片描述
      注:这里需要注意的是 @Validated 注解如果放在类或者放在方法参数前,UserDto 中的校验是无法生效,必须在参数前加 @Validated 注解

    • 对 @RequestBody 注解修饰的 List(List<UserDto> ) 对象进行校验

      该情形与上边介绍的第二种情形,主要是在接口定义那里的注解有区别,第二种情形使用的是 @Validated 注解,而这里需要使用 @Valid 注解(如果使用 @Validated 注解,则 List 中的 UserDto 里的注解(@NotEmpty、@Min 等等)将全都失效!!!! )
      在这里插入图片描述

嵌套验证的问题

上述的3种场景中没有涉及到嵌套验证,那什么是嵌套验证呢?简单的来说,就是一个需要验证的对象里嵌套了另外一个需要验证的对象,代码如下:
在这里插入图片描述
此时,我们在 UserFacade 的 add 方法的入参(UserDto)上使用 @Validated,就能对 UserDt o内嵌的 GroupDto 的参数进行验证了,代码如下:
在这里插入图片描述

总结

通过以上相关配置,只是基本的实现了参数校验。目前还没有对错误信息进行统一处理的需求,所以这里也就没有再进行更多的扩展开发了。但是,这里还是需要再明确一个问题,以上介绍的 3 个场景对应的错误返回格式是不相同的,主要分为两种:

  1. 对于场景【1】、场景【3】,会抛出 javax.validation.ConstraintViolationException 异常,如下:

    场景【1】:

    javax.validation.ConstraintViolationException: hello.arg0: must not be
    empty

    场景【3】:

    javax.validation.ConstraintViolationException:
    testParamList.arg0[1].loginName: must not be empty,
    testParamList.arg0[0].loginName: must not be empty

  2. 对于场景【2】,Response Status 将返回 400(Bad Request)

附上一些常用的校验注解

限制说明
@Null限制只能为null
@NotNull限制必须不为null
@AssertFalse限制必须为false
@AssertTrue限制必须为true
@DecimalMax(value)限制必须为一个不大于指定值的数字
@DecimalMin(value)限制必须为一个不小于指定值的数字
@Digits(integer,fraction)限制必须为一个小数,且整数部分的位数不能超过integer,小数部分的位数不能超过fraction
@Future限制必须是一个将来的日期
@Max(value)限制必须为一个不大于指定值的数字
@Min(value)限制必须为一个不小于指定值的数字
@Pattern(value)限制必须符合指定的正则表达式
@Size(max,min)限制字符长度必须在min到max之间
@Past验证注解的元素值(日期类型)比当前时间早,即必须是一个过去的日期
@NotEmpty验证注解的元素值不为null且不为空(字符串长度不为0、集合大小不为0)
@NotBlank验证注解的元素值不为空(不为null、去除首位空格后长度为0),不同于@NotEmpty,@NotBlank只应用于字符串且在比较时会去除字符串的空格
@Email验证注解的元素值是Email,也可以通过正则表达式和flag指定自定义的email格式
Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐