JSR303


JSR303

作用

往往我们会在前端进行一些表单校验等等,来确保传递的值是合法的。但是有一些手段可以绕过前端表单校验,所以我们需要在后端对javaBean进行校验。这就是用的JSR303。

大概步骤

1)、给相关的实体类上添加校验注解。比如我们这次要校验brand,那么就要给brandEntity的属性增加校验注解
在这里插入图片描述
BrandEntity中有这么一个属性,那我们就可以用@NotBlank来校验。

    /**
     * 品牌名
     */
    @NotBlank(message = "品牌名必须要提交"})  //提交的数据不能为空,空格都不行。
    private String name;

2)、需要给要调用校验的方法开启 校验功能,也就是在方法括号内添加@Valid注解。

比如在BrandController中会有一个方法,要调用BrandEntity来对其进行校验,那么我们就要去该方法开启校验功能。
在这里插入图片描述

3)、给校验后的bean紧跟一个BindingResult,就可以用它来接受校验结果

在这里插入图片描述

//        我不写BindingResult,那么异常就会被抛出去

        
        if (result.hasErrors()) {//如果有错误
            Map<String, String> map = new HashMap<>();
//        BindingResult 必须紧跟在要校验的对象后,用来接收校验结果
            List<FieldError> fieldErrors = result.getFieldErrors();
            for (FieldError error : fieldErrors) {
                //返回的错误提示,如果我们自己在注解上定义了就会返回自己定义的信息
                String message = error.getDefaultMessage();
//            获取错误属性的名字
                String field = error.getField();
                map.put(field, message);//把信息封装到map中
            }

            return R.error(400, "提交的数据不合法").put("data", map);
        } else {
            //没错误再去执行逻辑
            brandService.save(brand);
        }

4)、可以使用分组校验(多场景复杂校验):

  1. 给校验注解加上groups属性来指明它在那个组。@NotEmpty(groups = {AddGroup.class})

  2. 需要自己建立一个接口用来作为这个 组。因为本次拆分了服务,所以把这个组建立在common服务下。
    在这里插入图片描述

  3. 在校验方法上用@Validated({AddGroup.class}),不用原来的开启校验的注解了。 代表着我这个方法就校验分组为AddGroup组的属性。

  4. 没有指定gropus的属性在@Validated指定了分组的情况下无效,在@Validated没有指定分组的情况下有效。 也即@Validated指定了分组就只管校验属性也指定了同样分组的。如果@Validated没有指定分组,就管校验属性也没分组的

5)、自定义校验:
有些时候第三方提供的校验注解不能满足我们的业务需求,我们就需要自己来自定义校验注解。同上,这次还是写在common服务中。

假设这次想实现校验显示状态这个属性只能为0或者1

    /**
     * 显示状态[0-不显示;1-显示]
     */
    @ListValue(vals={0,1},groups = {AddGroup.class, UpdateStatusGroup.class})//自定义的校验注解
    private Integer showStatus;
  1. 编写一个自定义的校捡注解
    在这里插入图片描述
@Documented
/*validatedBy:指定这个注解用那个校验器来进行校验
   ListValueConstraintValidator.class:只能校验Integer类型的。
   如果以后还想校验Double型的,可以再写一个校验器,然后在{xx,xx}里写上就行了

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

//    默认去获取ValidationMessages.properties配置文件下的com.sl.common.valid.ListValue.message属性的值,来当作错误信息
//这里我们自己建立一个这个同名的配置文件再写上属性就好。
    String message() default "{com.sl.common.valid.ListValue.message}";

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

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


    int[] vals() default { };//定义一个int数组,用来存放哪些值是满足校验类型的。 也就是规定那些值可以通过校验
}
  1. 编写一个自定义的校验器
    在这里插入图片描述
/**
 * 自定义的校验注解器
 *
 * implements ConstraintValidator<ListValue,Integer>
 *     两个泛型:第一个:校验注解,第二个:指定我们要校验什么类型的数据
 */
public class ListValueConstraintValidator implements ConstraintValidator<ListValue,Integer> {

    private Set<Integer> set=new HashSet<>();

    //初始化方法
    @Override
    public void initialize(ListValue constraintAnnotation) {
        int[] vals=constraintAnnotation.vals();//拿到可以通过校验的值

        for (int i = 0; i <vals.length ; i++) {
            set.add(vals[i]);//把可以通过校验的值放到set中,方便isValid()的判断
        }
    }



    /**
     *    判断是否校验成功
     * @param value 需要校验的值
     * @param context
     * @return
     */
    @Override
    public boolean isValid(Integer value, ConstraintValidatorContext context) {

        return set.contains(value);//看看set集合中是否有value
    }
}
  1. 关联自定义的校验器和自定义的校验注解
    在自定义注解中:
@Constraint(validatedBy = { ListValueConstraintValidator.class})

因为以后要处理的异常很多,所以做一个统一的异常处理:@ControllerAdvice

在这里插入图片描述

//@ControllerAdvice
//@ResponseBody

/**
 * 集中处理所有异常
 */
@Slf4j
@RestControllerAdvice(basePackages = "com.sl.gulimall.product.controller")  //相当于上面两个的合体
public class GulimallException {

    @ExceptionHandler(value = MethodArgumentNotValidException.class)//代表捕获MethodArgumentNotValidException异常
    public R handleVaildException(MethodArgumentNotValidException e) {
        log.error("数据出现问题{},异常类型{}", e.getMessage(), e.getClass());

        BindingResult result = e.getBindingResult();//拿到异常信息
        Map<String, String> errorMap = new HashMap<>();

        List<FieldError> fieldErrors = result.getFieldErrors();
        for (FieldError error : fieldErrors) {
            //把出现异常的属性,和异常信息放入map中
            errorMap.put(error.getField(),error.getDefaultMessage());
        }

        //在gulimall-common下统一了异常的种类
        return R.error(baseCode.VAILD_EXCEPTION.getCode(),baseCode.VAILD_EXCEPTION.getMsg()).put("data",errorMap);
    }


    @ExceptionHandler(value = Throwable.class)//代表捕获所有异常
    public R handleException(MethodArgumentNotValidException e) {


        return R.error(baseCode.UNNKNOW_EXCEPTION.getCode(),baseCode.UNNKNOW_EXCEPTION.getMsg());
    }
}

注意:用了统一的异常处理那么就不要用BindingResult了,这样才会把异常抛出去,进而才能被我们自己写的处理异常的类捕获到。
在这里插入图片描述


文章作者: fFee-ops
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 fFee-ops !
评论
  目录