数据校验(前端数据校验、JSR303校验)

117 阅读4分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第13天,点击查看活动详情

数据校验

前端数据校验

就是规定添加的属性要符合规定,不然会出现想不到的异常!

例如:添加品牌选项框中,设置检索首字母那么我们就要规定首字母不能是多个字母只能是a-z或A-Z之间的一个

那么我们就可以对这个输入框进行绑定,如下

image-20220803201346675

实现效果如下:

image-20220803201146001

JSR303数据校验

后端的处理前端传来的数据时,虽然前端已做限制但是还不够严谨,例如我们可以跳过页面通过一些工具直接发送请求也可以完成添加等操作,所以后端也需要做数据校验!

java中也提供了一系列的校验方式,它这些校验方式在“javax.validation.constraints”包中,@Email,@NotNull等注解。

一、添加依赖

后面可能其他模块也能用到,所以这里把依赖添加到common模块

<!--jsr3参数校验器-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
    <version>2.3.2.RELEASE</version>
</dependency>

这个依赖提供了NotNull,@NotBlank和@NotEmpty这些判断

二、给需要校验的bean添加注解

/**
 * 品牌
 * 
 * @author xiaocai
 * @email mildcaq@gmail.com
 * @date 2022-07-27 21:05:30
 */
@Data
@TableName("pms_brand")
public class BrandEntity implements Serializable {
   private static final long serialVersionUID = 1L;
​
   /**
    * 品牌id
    */
   @TableId
   private Long brandId;
   /**
    * 品牌名
    */
   @NotBlank
   private String name;
   /**
    * 品牌logo地址
    */
   private String logo;
   /**
    * 介绍
    */
   private String descript;
   /**
    * 显示状态[0-不显示;1-显示]
    */
   @NotNull
   private Integer showStatus;
   /**
    * 检索首字母
    */
   @NotEmpty
   private String firstLetter;
   /**
    * 排序
    */
   @NotNull
   @Min(0)
   private Integer sort;
​
}

未在控制类中指定方法开启校验时如下:

image-20220805214722499

controller中给请求方法加校验注解@Valid,开启校验,

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

这里我错误信息只返回是Bad Request

视频中老师所讲的是有详细信息的,这个差异应该是版本原因不影响!

image-20220805214850184

这种返回的错误结果并不符合我们的业务需要。我们想让捕捉这个错误的详细信息,并且能够统一返回我们自定义的信息!

三、通过BindResult捕获校验结果

修改内容如下:

@RequestMapping("/save")
public R save(@Valid @RequestBody BrandEntity brand,BindingResult result){
    if( result.hasErrors()){
        Map<String,String> map=new HashMap<>();
        //1.获取错误的校验结果
        result.getFieldErrors().forEach((item)->{
            //获取发生错误时的message
            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();
}

再次测试

好啦,这下把错误信息都捕获到喽!

image-20220805215355999

但是,这种是针对于该请求设置了一个内容校验,如果针对于每个请求都单独进行配置,显然不是太合适,实际上可以统一的对于异常进行处理

四、统一异常处理

可以使用SpringMvc所提供的@ControllerAdvice,通过“basePackages”能够说明处理哪些路径下的异常。

(1)抽取一个异常处理类

详细信息都写在了注释里,可以作为参考!!!


@Slf4j
@RestControllerAdvice(basePackages = "com.caq.mall.product.controller")
public class MallExceptionAdvice {
    //指定的包下所有的校验异常都会被这个方法捕捉
    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    public R handleValidException(MethodArgumentNotValidException exception) {
        //定义map,存放所有错误信息
        Map<String, String> map = new HashMap<>();
        //通过BindResult捕获校验结果
        BindingResult bindingResult = exception.getBindingResult();
        //遍历校验结果中所有字段的错误,字段为key,错误信息为value存放到map中
        bindingResult.getFieldErrors().forEach(fieldError -> {
            String message = fieldError.getDefaultMessage();
            String field = fieldError.getField();
            map.put(field, message);
        });
//        控制台打印错误信息
        log.error("数据校验出现问题{},异常类型{}", exception.getMessage(), exception.getClass());
//        返回错误结果,并显示所有错误的数据
        return R.error(400, "数据校验出现问题").put("data", map);
    }
}

(2)测试: http://localhost:88/api/product/brand/save

接下来我们去掉我们控制类中save方法的校验,看看统一异常处理能否生效

还是可以哦!

image-20220805221126581

(3)错误状态码

正规开发过程中,错误状态码有着严格的定义规则

/***
 * 错误码和错误信息定义类
 * 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;
    }
}

(4)默认异常处理

上面的统一异常处理只是针对了校验相关的错误,那么如果是其他异常呢?

那就再来个默认的异常处理呗

   @ExceptionHandler(value = Throwable.class)
    public R handleException(Throwable throwable){
        log.error("未知异常{},异常类型{}",throwable.getMessage(),throwable.getClass());
        return R.error(BizCodeEnum.UNKNOW_EXEPTION.getCode(),BizCodeEnum.UNKNOW_EXEPTION.getMsg());
    }

\