天天看点

Spring参数校验之Bean的分组校验@Validated

利用好javax.validation.groups.Default.class这个默认分组

一、建立不同的分组

注意建立的是接口,继承Default

AddGroup

public interface AddGroup extends Default {
}
           
UpdateGroup
           
public interface UpdateGroup extends Default {
}
           

LogoGroup 测试用

public interface LogoGroup extends Default {
}
           

二、在Bean上加校验注解

BrandEntity 品牌实体类

@Data
@TableName("pms_brand")
public class BrandEntity implements Serializable {
    private static final long serialVersionUID = 1L;

    /**
     * 品牌id
     */
    @Null(message = "新增不能指定品牌id", groups = AddGroup.class)
    @NotNull(message = "修改必须指定品牌id", groups = UpdateGroup.class)
    @TableId
    private Long brandId;
    /**
     * 品牌名
     */
    @NotBlank(message = "品牌名不能为空")
    private String name;
    /**
     * 品牌logo地址
     */
    @NotBlank(message = "品牌logo地址不能为空", groups = {UpdateGroup.class, LogoGroup.class})
    @URL(message = "logo地址应该是一个网址")
    private String logo;
    /**
     * 介绍
     */
    private String descript;
    /**
     * 显示状态[0-不显示;1-显示]
     */
    @NotNull(message = "显示状态不能为空")
    @Range(min = 0, max = 1, message = "显示状态的值应该为[0-不显示;1-显示]")
    private Integer showStatus;
    /**
     * 检索首字母
     */
    @Pattern(regexp = "^[a-zA-Z]$", message = "检索首字母必须是一个字母")
    private String firstLetter;
    /**
     * 排序
     */
    @Min(value = 0, message = "排序字段的值应该为大于等于0的整数")
    private Integer sort;
}
           

三、在Controller的方法参数前加@Validated

注意:如果是要分组校验,那么只能加@Validated,不能加@Valid

/**
     * 保存
     */
    @RequestMapping("/save")
    public R save(@Validated({AddGroup.class}) @RequestBody BrandEntity brand){
        brandService.save(brand);
        return R.ok();
    }

    /**
     * 修改
     */
    @RequestMapping("/update")
    public R update(@Validated({UpdateGroup.class}) @RequestBody BrandEntity brand){
        brandService.updateById(brand);
        return R.ok();
    }

    /**
     * 修改2
     */
    @RequestMapping("/update2")
    public R update2(@Validated({LogoGroup.class}) @RequestBody BrandEntity brand){
        brandService.updateById(brand);
        return R.ok();
    }
           

因为AddGroup继承了Default.class,因此save方法校验BrandEntity里这些信息:

  1. 配置了分组信息groups,且groups里包含AddGroup的字段;
  2. 没有配置分组信息groups的字段;

以brandId和logo为例:

brandId:

@Null(message = "新增不能指定品牌id", groups = AddGroup.class)
@NotNull(message = "修改必须指定品牌id", groups = UpdateGroup.class)
           
  1. save方法只会校验@Null
  2. update方法只会校验@NotNull
  3. update2方法不会校验

logo:

@URL(message = "logo地址应该是一个网址")
 @NotBlank(message = "品牌logo地址不能为空", groups = {UpdateGroup.class, LogoGroup.class})
           
  1. save方法只会校验@URL
  2. update方法会校验@URL和@NotBlank
  3. update2方法会校验@URL和@NotBlank

注:

因为@URL没有配置分组信息groups,默认是Default分组,又因为AddGroup等都继承了Default.class,因此无论Controller里有没有分组,使用的分组是哪一个,@URL每次都会加载到,只有logo有值,都会去校验。

继承了Default.class的好处就体现在这里,只要是无论什么分组都需要校验的字段,不需要去配置分组信息。否则就需要在groups里把所有的分组写上去,非常麻烦,且不美观。

只要字段上配置了分组信息groups,那么只有当Controller里的分组在groups里(Controller里没有分组,那么默认是Default分组)的时候,才会去校验。

四、配置全局异常

ExceptionController
           
@Slf4j
@RestControllerAdvice(basePackages = {"com.xxx.xxx"})
public class ExceptionController {
    // 数据校验异常
    @ExceptionHandler(value = {MethodArgumentNotValidException.class})
    public R handleValidException(MethodArgumentNotValidException e) {
        log.warn("传入参数校验不通过:{},异常类型:{}",e.getMessage(),e.getClass());
        Map<String, String> map = new HashMap<>();
        BindingResult bindingResult = e.getBindingResult();
        bindingResult.getFieldErrors().forEach(fieldError -> {
            map.put(fieldError.getField(), fieldError.getDefaultMessage());
        });

        return R.error(ResultCodeEnum.VALID_EXCEPTION).put("data", map);
    }

    @ExceptionHandler(Throwable.class)
    public R handleException(Throwable throwable){

        return R.error(ResultCodeEnum.UNKNOW_EXCEPTION);
    }

}
           

注意:如果是在公共工程里添加统一异常处理类,那么在其它微服务的启动类上要加上注解扫描的路径,比如:

@SpringBootApplication(scanBasePackages = {"com.xxx.xxx"})
           

五、其它

  • 级联校验:entity A里的字段是另一个entity B,如果需要级联校验entity B,那么需要在entity A的字段上加@Valid注解
  • 对Controller里的方法的多个参数进行校验(扁平化参数):在Controller类上加注解@Validated
@RestController
@RequestMapping
@Validated
public class HelloController {
    @PutMapping("/hello/id/{id}/status/{status}")
    public Object helloGet(@Max(5) @PathVariable Integer id, @Min(5) @PathVariable Integer status) {
        return "hello world";
    }
}
           
  • @Valid和Validated的区别
  • 常用的校验注解