SpringBoot学习 -JRS303 做后端数据校验整合

JRS303 做后端数据校验

1. 引入springboot整合jrs依赖

<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-validation -->
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-validation</artifactId>
</dependency>

2. 普通校验

1、给bean添加校验注解

@Data
@TableName("pms_brand")
public class BrandEntity implements Serializable {
    
    
   private static final long serialVersionUID = 1L;
   /**
    * 品牌id
    */
   @Null(message = "新增不能指定ID")
   @NotNull(message = "修改必须指定ID")
   @TableId
   private Long brandId;
   /**
    * 品牌名
    */
   @NotBlank(message = "品牌名必须提交")
   private String name;
   /**
    * 品牌logo地址
    */
   @NotBlank(message = "品牌logo地址必须提交")
   @URL(message = "logo必须是一个合法的URL地址")
   private String logo;
   /**
    * 介绍
    */
   private String descript;
   /**
    * 显示状态[0-不显示;1-显示]
    */
   //自定义注解
   private Integer showStatus;
   /**
    * 检索首字母
    */
   @NotBlank(message = "检索首字母必须提交")
   @Pattern(regexp = "^[a-zA-Z]$",message = "检索首字母必须是一个字母")
   private String firstLetter;
   /**
    * 排序
    */
   @NotNull(message = "排序必须提交")
   @Min(value = 0,message = "排序必须大于等于0")
   private Integer sort;
}

2、在controller层方法的参数上开启校验功能

可以给校验的参数bean在controller的方法参数紧跟一个BindResult参数 可以自定义错误处理

 @RequestMapping("/save")
    public R save(@Valid @RequestBody BrandEntity brand,BindingResult bindingResult){
    
    
       if(bindingResult.hasErrors()){
    
    
           //获取校验的错误结果
           Map<String,String > map = new HashMap<>();
            bindingResult.getFieldErrors().forEach(item ->{
    
    
               String message = item.getDefaultMessage();
                String field = item.getField();
               map.put(field,message);
           });
            return R.error(400,"提交的数据不合法").put("data",map);
        }else{
    
    
            brandService.save(brand);
            return R.ok();
        }
        brandService.save(brand);
        return R.ok();
    }

3、统一异常处理

避免在controller层处理异常,使用统一异常处理@RestControllerAdvice

   @RequestMapping("/save")
    public R save(@Valid @RequestBody BrandEntity brand){
    
    
        //不添加bindingresult时异常会抛出
        brandService.save(brand);
        return R.ok();
    }

新建异常处理类捕获处理异常

/**
 * 集中处理所有异常
 */
@Slf4j
@RestControllerAdvice(basePackages = "com.rwp.gulimail.product.controller")
public class GulimailExceptionControllerAdvice {
    
    
    
    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    public R handleVaildException(MethodArgumentNotValidException e){
    
    
        log.error("数据校验出现问题:"+e.getMessage()+e.getClass());
        BindingResult bindingResult = e.getBindingResult();
        //获取校验的错误结果
        Map<String,String > map = new HashMap<>();
        bindingResult.getFieldErrors().forEach(item ->{
    
    
            String message = item.getDefaultMessage();
            String field = item.getField();
            map.put(field,message);
        });
        return R.error(BizCodeEnume.VALID_EXCEPTION.getCode(),BizCodeEnume.VALID_EXCEPTION.getMessage()).put("data",map);
    }
    
    //全局异常
    @ExceptionHandler(value = Throwable.class)
    public R handleException(Throwable throwable){
    
    
        return R.error(BizCodeEnume.UNKONW_EXCEPTION.getCode(),BizCodeEnume.UNKONW_EXCEPTION.getMessage());
    }
}

3. 分组校验(多场景复杂校验)

1、新建场景接口作为标志(不做任何处理)

public interface AddGroup {
    
    }
public interface UpdateGroup {
    
    }

2、实体类的校验标签添加groups属性

@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 = "品牌名必须提交",groups = {
    
    AddGroup.class,UpdateGroup.class})
   private String name;
   /**
    * 品牌logo地址
    */
   @NotBlank(message = "品牌logo地址必须提交" ,groups = {
    
    AddGroup.class})
   @URL(message = "logo必须是一个合法的URL地址" ,groups = {
    
    AddGroup.class,UpdateGroup.class})
   private String logo;
   /**
    * 介绍
    */
   private String descript;
   /**
    * 显示状态[0-不显示;1-显示]
    */
   private Integer showStatus;
   /**
    * 检索首字母
    */
   @NotBlank(message = "检索首字母必须提交" ,groups = {
    
    AddGroup.class})
   @Pattern(regexp = "^[a-zA-Z]$",message = "检索首字母必须是一个字母" ,groups = {
    
    AddGroup.class,UpdateGroup.class})
   private String firstLetter;
   /**
    * 排序
    */
   @NotNull(message = "排序必须提交",groups = {
    
    AddGroup.class})
   @Min(value = 0,message = "排序必须大于等于0" ,groups = {
    
    AddGroup.class,UpdateGroup.class})
   private Integer sort;

}

3、controller的方法参数指定属于哪一个groups @Validated

默认没有指定分组的校验注解 在分组的校验情况下不生效,只会在@Valid生效

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

4. 自定义校验

引入依赖

<dependency>
    <groupId>javax.validation</groupId>
    <artifactId>validation-api</artifactId>
    <version>2.0.1.Final</version>
</dependency>

效果

给BrandEntity的showstatus属性添加自定义校验,目的:showstatus的值只能为0或1

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

   /**
    * ..............
    */

   /**
    * 显示状态[0-不显示;1-显示]
    */
   //自定义注解
   @ListValue(values={
    
    0,1},groups = {
    
    AddGroup.class})
   private Integer showStatus;
   /**
    * ..........
    */

}

1、编写一个自定义的检验注解

@Documented
@Constraint(
        validatedBy = {
    
    ListValueConstraintValidator.class}
)
@Target({
    
    ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
public @interface ListValue {
    
    
    //前三个是jrs303的规范
    String message() default "{com.rwp.common.valid.ListValue.message}";

    Class<?>[] groups() default {
    
    };

    Class<? extends Payload>[] payload() default {
    
    };

    int[] values() default {
    
    }; //自己添加的属性
}

2、编写一个自定义的校验器

public class ListValueConstraintValidator implements ConstraintValidator<ListValue,Integer>{
    
    
    //泛型第一个为注解类型,第二个为注解标注的属性的类型
    private Set<Integer> set = new HashSet<>();
    //初始化方法
    @Override
    public void initialize(ListValue constraintAnnotation) {
    
    
        int[] values = constraintAnnotation.values();
        for (int value: values
             ) {
    
    
            set.add(value);
        }
    }
    //判断是否校验成功
    @Override
    public boolean isValid(Integer integer, ConstraintValidatorContext constraintValidatorContext) {
    
    
        if(set.contains(integer)){
    
    
            return true;
        }else {
    
    
            return false;
        }
    }
}

3、关联自定义的校验器和自定义的校验注解

1、校验器实现的接口泛型指明注解属性

public class ListValueConstraintValidator implements ConstraintValidator<ListValue,Integer>{
    
    

2、注解使用@Constraint添加校验器

@Documented
@Constraint(
        validatedBy = {
    
    ListValueConstraintValidator.class}
)
@Target({
    
    ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
public @interface ListValue {
    
    

猜你喜欢

转载自blog.csdn.net/weixin_44634197/article/details/108399307