JSR303后端校验&统一异常处理

文章讲述了在Java中如何使用校验注解对实体类属性进行验证,包括自定义错误消息、使用@Valid和BindingResult获取校验结果。同时,介绍了如何通过创建ExceptionControllerAdvice进行统一异常处理,以及分组校验和自定义校验注解的实现方法。
摘要由CSDN通过智能技术生成

使用步骤

  1. 给实体类属性添加校验注解(校验注解都在javax.validation.constraints包下),默认的message都在ValadationMessages_zh_CN.properties,不满意可以自定义message提示,如
    @NotBlank(message = "品牌名不能为空")
  2.  在Controller需要校验的实体类前加注解@Valid,如
    public R save(@Valid @RequestBody BrandEntity brand) {}
  3.  在校验的实体类后紧跟BindingResult就可以获得校验结果,如
    public R save(@Valid @RequestBody BrandEntity brand, BindingResult result) {}
  4.  完整例子
     /**
         * 保存
         */
        @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)->{
                    //FieldError 获取到错误提示
                    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();
        }
    postman测试
    localhost:88/api/product/brand/save
    
    {
        "msg": "提交的数据不合法",
        "code": 400,
        "data": {
            "name": "品牌名必须提交"
        }
    }

统一异常处理 

不使用BindingResult感知异常,有异常时,异常就会被抛出

基本使用

用统一异常处理来处理Controller里数据校验的逻辑

  1. 新建包exception、新建类ExceptionControllerAdvice
  2. 加了统一异常处理后,controller里参数中的BindingResult删掉,数据校验逻辑删掉
/**
 * 集中处理所有异常
 */
@Slf4j
//@ResponseBody
//@ControllerAdvice(basePackages = "com.atguigu.gulimall.product.controller")
@RestControllerAdvice(basePackages = "com.atguigu.gulimall.product.controller")
public class ExceptionControllerAdvice {


    @ExceptionHandler(value=Exception.class)  //方法可以处理的异常
    public R handleVaildException(Exception e){
        log.error("数据校验出现问题{},异常类型:{}",e.getMessage(),e.getClass());
        //BindingResult bindingResult = e.getBindingResult();
        return R.error();
        
    }
}
/**
 * 集中处理所有异常
 */
@Slf4j
//@ResponseBody
//@ControllerAdvice(basePackages = "com.atguigu.gulimall.product.controller")
@RestControllerAdvice(basePackages = "com.atguigu.gulimall.product.controller")
public class ExceptionControllerAdvice {


    @ExceptionHandler(value= MethodArgumentNotValidException.class)
    public R handleVaildException(MethodArgumentNotValidException e){
        log.error("数据校验出现问题{},异常类型:{}",e.getMessage(),e.getClass());
        BindingResult bindingResult = e.getBindingResult();

        Map<String,String> errorMap = new HashMap<>();
        bindingResult.getFieldErrors().forEach((fieldError)->{
            errorMap.put(fieldError.getField(),fieldError.getDefaultMessage());
        });
        //return R.error(BizCodeEnume.VAILD_EXCEPTION.getCode(),BizCodeEnume.VAILD_EXCEPTION.getMsg()).put("data",errorMap);
        return R.error(400,"数据校验出现问题").put("data",errorMap);
    }

枚举类怎么写?需要会

分组校验

实体类属性注解内添加注解属性groups,给注解分组

1.编写分组接口AddGroup、UpdateGroup,接口写在common模块valid包下

public interface AddGroup {
}

实体类上的注解 

	/**
	 * 品牌id
	 */
	@NotNull(message = "修改必须指定品牌id",groups = {UpdateGroup.class})
	@Null(message = "新增不能指定id",groups = {AddGroup.class})
    @NotBlank(message="",groups= {AddGroup.class, UpdateGroup.class})
	@TableId
	private Long brandId;

2.Controller里原本@Valid替换成@Validated,此注解参数可指定一个或多个校验分组

使用@Validated后,实体类属性校验注解如果没有添加groups,那么实体类属性上的校验不生效,这种情况只会在@Validated不指定校验分组的时候生效

@Validated({AddGroup.class})

自定义校验注解

  1. 引入依赖
    <dependency>
        <groupId>javax.validation</groupId>
        <artifactId>validation-api</artifactId>
        <version>2.0.1.Final</version>
    </dependency>
  2. 编写一个自定义校验注解
    @Documented
    @Constraint(validatedBy = { ListValueConstraintValidator.class })  //校验器
    @Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
    @Retention(RUNTIME)
    public @interface ListValue {
        String message() default "{com.atguigu.common.valid.ListValue.message}"; //自定义校验注解的全类名  
    
        Class<?>[] groups() default { };
    
        Class<? extends Payload>[] payload() default { };
    
        int[] vals() default { };
    }
    

    类路径下新建ValidationMessages.properties

    com.atguigu.common.valid.ListVAlue.message="必须提交指定的值"
  3. 编写一个自定义校验器
    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 val : vals) {
                set.add(val);
            }
    
        }
    
        //判断是否校验成功
    
        /**
         *
         * @param value 需要校验的值
         * @param context
         * @return
         */
        @Override
        public boolean isValid(Integer value, ConstraintValidatorContext context) {
    
            return set.contains(value);
        }
    }

  4. 关联自定义校验注解和自定义校验器
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值