Loading

JSR303数字校验

天空和我的中间,只剩下倾盆的思念

简单校验

使用示例:

  1. 引入对应的校验依赖

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-validation</artifactId>
   <version>2.3.2.RELEASE</version>
</dependency>
  1. 在需要校验的业务字段上添加相应的注解,如:

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

/**
* 品牌id
*/
@TableId
private Long brandId;
/**
* 品牌名
*/
@NotBlank(message = "品牌名必须提交")
private String name;
/**
* 品牌logo地址
*/
@NotEmpty
@URL(message = "logo必须是一个合法的url地址")
private String logo;
/**
* 介绍
*/
private String descript;
/**
* 显示状态[0-不显示;1-显示]
*/
private Integer showStatus;
/**
* 检索首字母
*/
@NotEmpty
@Pattern(regexp = "/^[a-zA-Z]$/", message = "检索首字母必须是一个字母")
private String firstLetter;
/**
* 排序
*/
@NotNull
@Min(value = 0, message = "排序必须大于等于0")
private Integer sort;

}
  1. 在需要校验的方法上(一般是controller方法)添加@Valid注解,在后面追加BindingResult类,相当于错误结果都追加到这个类中

@RequestMapping("/save")
   //@RequiresPermissions("product:brand:save")
   public R save(@Valid @RequestBody BrandEntity brand, BindingResult result){
       if (result.hasErrors()){
           Map<String, String> map = new HashMap<>();
           //1、获取校验的结果
           result.getFieldErrors().forEach((item)->{
               //获取到错误提示
               String message = item.getDefaultMessage();
               //获取到错误属性的名字
               String field = item.getField();
               map.put(field, message);
          });
           return R.error().put("data", map);
      }else{
           brandService.save(brand);
      }
       return R.ok();
  }

统一异常处理

使用示例:

  1. 定义特定错误的状态码,common模块中单独定义一个常量类,用来存储这些错误状态码

/***
* 错误码和错误信息定义类
* 1. 错误码定义规则为5为数字
* 2. 前两位表示业务场景,最后三位表示错误码。例如:100001。10:通用 001:系统未知异常
* 3. 维护错误码后需要维护错误描述,将他们定义为枚举形式
* 错误码列表:
* 10: 通用
*     001:参数格式校验
* 11: 商品
* 12: 订单
* 13: 购物车
* 14: 物流
*/
public enum BizCodeEnum {

   UNKNOW_EXEPTION(10000,"系统未知异常"),

   VALID_EXCEPTION( 10001,"参数格式校验失败");

   private int code;
   private String msg;
   BizCodeEnum(int code, String msg) {
       this.code = code;
       this.msg = msg;
  }

   public int getCode() {
       return code;
  }

   public String getMsg() {
       return msg;
  }
}
  1. 在product里面新建异常拦截类GulimallExceptionControllerAdvice,用来集中处理所有异常 @ExceptionHandler

@RestControllerAdvice(basePackages = "com.xmh.gulimall.product.controller")
public class GulimallExceptionControllerAdvice {

   // 处理数据校验异常
   @ExceptionHandler(value = MethodArgumentNotValidException.class)
   public R handleVaildException(MethodArgumentNotValidException e){
       BindingResult bindingResult = e.getBindingResult();
       Map<String, String> errorMap = new HashMap<>();
       bindingResult.getFieldErrors().forEach((fieldError)->{
           errorMap.put(fieldError.getField(), fieldError.getDefaultMessage());
      });

       return R.error(BizCodeEnume.VALID_EXCEPTION.getCode(),BizCodeEnume.VALID_EXCEPTION.getMsg()).put("data", errorMap);
  }

   //处理全局异常
   @ExceptionHandler(value = Throwable.class)
   public R handleException(Throwable throwable){
       return R.error(BizCodeEnume.UNKNOW_EXEPTION.getCode(), BizCodeEnume.UNKNOW_EXEPTION.getMsg());
  }

}

思考:这样可以做到单个模块的全局异常拦截,但是每一个模块都要单独定义一个感觉不优雅。

改进:可不可以在common模块定义一个所有模块能共用的,后面要用的话,直接在每一个controller类继承这个公共类即可。如:

public class BaseController 
{

   public static final Logger LOGGER = LoggerFactory.getLogger(BaseController.class);

   /**
    * 统一异常处理
    * @param request
    * @param response
    * @param exception
    */
   @ExceptionHandler
   public R exceptionHandler(HttpServletRequest request, HttpServletResponse response, Exception exception)
  {
       LOGGER.error("统一异常处理:", exception);
       request.setAttribute("ex", exception);
       if (null != request.getHeader("X-Requested-With")
               && request.getHeader("X-Requested-With").equalsIgnoreCase("XMLHttpRequest"))
      {
           request.setAttribute("requestHeader", "ajax");
      }
       // 业务异常捕获
       if (exception instanceof NormalRuntimeException)
      {
           exception.printStackTrace();
           return R.fail("操作失败!" + exception.getMessage());
      }
       // 自定义异常捕获
       if (exception instanceof CustomException)
      {
           exception.printStackTrace();
           return R.fail(exception.getMessage());
      }
       return R.fail("未知异常!请稍后再试或联系管理员!");
  }
}

分组校验

使用示例:

  1. 在common中新建valid包,里面新建两个空接口AddGroup,UpdateGroup用来分组

  1. 在需要分组校验的字段上标注分组,如:

@NotEmpty
@NotBlank(message = "品牌名必须非空",groups = {UpdateGroup.class,AddGroup.class})
private String name;
  1. 校验方法上的@Valid注解换成@Validated注解,并标记好分组

@RequestMapping("/save")
public R save(@Valided({AddGroup.class}) @RequestBody BrandEntity brand){
...
}

自定义校验

使用示例:

  1. 编写一个自定义注解接口,如:

@Documented
@Constraint(validatedBy = { ListValueConstraintValidator.class }) //指定校验器 这里指定我们自定义的ListValueConstraintValidator
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
public @interface ListValue {
   // 使用我们自己定义的错误信息 在common模块下的resources中
   // String message() default "{com.atguigu.common.valid.ListValue.message}";
   String message() default "值必须是0或者1";

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

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

   //预先准备的值 vals={0,1}
   int[] vals() default {};
}
  1. 编写一个自定义注解校验器,如

public class ListValueConstraintValidator implements ConstraintValidator<ListValue,Integer> {
   private Set<Integer> set=new HashSet<>();
   @Override
   public void initialize(ListValue constraintAnnotation) {
       int[] value = constraintAnnotation.vals();
       for (int i : value) {
           set.add(i);
      }

  }

   @Override
   public boolean isValid(Integer value, ConstraintValidatorContext context) {
       return  set.contains(value);
  }
}
  1. 关联检验接口与校验器

@Constraint(validatedBy = {ListValueConstraintValidator.class})
  1. 在需要的字段上进行添加

@ListValue(vals = {0, 1},groups = {AddGroup.class, UpdateGroup.class})
private Integer showStatus;
 
posted @ 2022-09-19 16:03  你比从前快乐;  阅读(109)  评论(0编辑  收藏  举报