天天看點

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的差別
  • 常用的校驗注解