JSR303数据校验 Spring统一异常处理

1 篇文章 0 订阅

JSR303进行数据校验

1.给Bean添加校验注解
可以多个注解校验

@NotBlank(message = "品牌名不能为空")
private String name;

//不能为空 + 自定义规则
@NotEmpty 
@Pattern(regexp = "!/^[a-zA-Z]$/",message = "检索首字母必须是一个字母")
private String firstLetter;

//不能为空 +  最小值
@NotEmpty 
@Min(value = 0,message = "排序最小值是0")
private Integer sort;

Tips:@JsonInclude(JsonInclude.Include.NON_EMPTY) 标注的字段返回不为空时才会返回

在这里插入图片描述
2.在提交数据时开启数据校验功能

    /**
     * 保存
     * @Valid 注解告诉springmvc需要校验
     */
    @RequestMapping("/save")
    public R save(@Valid @RequestBody BrandEntity brand) {
        brandService.save(brand);

        return R.ok();
    }

3.校验错误时会有默认的响应
在被校验的Bean后紧跟BindResult 可以获取校验结果

/**
     * 保存
     * @Valid 注解告诉springmvc需要校验
     * BindingResultresult 封装了校验结果
     */
    @RequestMapping("/save")
    public R save(@Valid @RequestBody BrandEntity brand, BindingResultresult)  {
        brandService.save(brand);

        return R.ok();
    }

测试:

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

返回结果

{
	"msg": "提交的数据不合法",
	"code": 400,
	"data": {
		"name": "品牌名不能为空"
	}
}

统一异常处理类

@ControllerAdvice

  • ControllerAdvice本质上是一个Component
  • 就是aop思想的一种实现,你告诉我需要拦截规则,我帮你把他们拦下来,具体你想做更细致的拦截筛选和拦截之后的处理,通过@ExceptionHandler、@InitBinder 或 @ModelAttribute这三个注解以及被其注解的方法来自定义。
  • @ControllerAdvice 配合 @ExceptionHandler 实现全局异常处理
  • 全局数据预处理
  • 全局数据绑定 @ControllerAdvice修饰的类中数据 在每一个controller都能访问
//放入数据
@ControllerAdvice
public class MyGlobalExceptionHandler {
    @ModelAttribute(name = "md")
    public Map<String,Object> mydata() {
        HashMap<String, Object> map = new HashMap<>();
        map.put("age", 99);
        map.put("gender", "男");
        return map;
    }
}
//取出数据
@RestController
public class HelloController {
    @GetMapping("/hello")
    public String hello(Model model) {
        Map<String, Object> map = model.asMap();
        System.out.println(map);
        int i = 1 / 0;
        return "hello controller advice";
    }
}

1.抽取一个异常处理类

package cn.cloud.xmall.product.exception;

import cn.cloud.xmall.common.exception.ExceptionEnume;
import cn.cloud.xmall.common.utils.R;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import java.util.HashMap;
import java.util.Map;

/**
 * 集中处理所有异常
 * 指定的Controller的异常被抛出时会被拦截
 * basePackages 处理哪个Controller的异常
 */
@Slf4j
@RestControllerAdvice(basePackages = "cn.cloud.xmall.product.controller")
public class XmallExceptionControllerAdvice {

    /**
     * 此方法处理数据校验异常
     * @ExceptionHandler 告诉springmvc这个注解标注的这个方法能处理什么异常
     */
    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    public R handleVailException(MethodArgumentNotValidException e){

        BindingResult result = e.getBindingResult();
        log.error("数据校验异常"+e.getMessage()+e.getClass());
        Map<String,String> map = new HashMap<>();
            //获取校验的错误结果
            result.getFieldErrors().forEach((item) -> {
                //错误提示消息
               String message =  item.getDefaultMessage();
                //获取错误的字段
               String field =  item.getField();
                map.put(field, message);
            });

        return R.error(ExceptionEnume.VATLD_EXCEPTION.getCode(),ExceptionEnume.VATLD_EXCEPTION.getMsg()).put("data",map);
    }

    /**
     * 最大的异常处理 处理任意类型
     * @return
     */
    @ExceptionHandler(value = Exception.class)
    public R handleException(){

        return R.error(ExceptionEnume.UNKNOW_EXCEPTION.getCode(),ExceptionEnume.UNKNOW_EXCEPTION.getMsg());
    }
}

定义异常信息枚举类

package cn.cloud.xmall.common.exception;

/**
 * 系统错误代码枚举类
 */
public enum ExceptionEnume {
    UNKNOW_EXCEPTION(10001,"系统为止异常"),
    VATLD_EXCEPTION(10001,"参数格式校验失败");

    private Integer code;
    private String msg;

    ExceptionEnume(Integer code,String msg){
        this.code = code;
        this.msg = msg;
    }

    public Integer getCode() {
        return code;
    }

    public String getMsg() {
        return msg;
    }
}

数据分组校验

在不同情况下触发的校验规则可以使用分组校验来区分
可以完成多场景的复杂校验

@Valid 为JSR303规范提供的注解 @Validated为spring框架提供的注解

1.给校验注解标注,什么情况下需要校验

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

@Validated 可以指定一个或多个校验分组

2.将@Valid 注解修改为@Validated 并指定校验分组(可以指定多个)

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

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

自定义注解校验

现已有的注解无法满足校验需求

1.编写自定义校验注解

需要引入依赖

<!--   校验API     -->
<dependency>
    <groupId>javax.validation</groupId>
    <artifactId>validation-api</artifactId>
    <version>2.0.1.Final</version>
</dependency>
package cn.cloud.xmall.common.valid;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

/**
 * 注解复制其他校验注解就可
 */
@Documented
//自定义校验器对象
@Constraint(validatedBy = { ListValueConstraintValidator.class })
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
public @interface ListValue {

    //变量为校验注解的全类名 此参数必须
    String message() default "{cn.cloud.xmall.common.valid.ListValue.message}";
    //此参数必须
    Class<?>[] groups() default { };
    //此参数必须
    Class<? extends Payload>[] payload() default { };

    //value属性 默认空
    int[] vals() default { };

}

2.编写自定义校验器

@Constraint(validatedBy = { }) validatedBy = { } 可以指定使用什么来校验

public interface ConstraintValidator<A extends Annotation, T> 是一个接口 泛型一是注定注解,第二个指定校验什么类型的数据 ,自定义校验器需实现此接口

package cn.cloud.xmall.common.valid;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.HashSet;
import java.util.Set;

public class ListValueConstraintValidator implements ConstraintValidator<ListValue, Integer> {


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

    /**
     * 初始化方法
     * 会将@ListValue 注解的详细信息获取
     * vals 即 @ListValue(vals = {0,1})
     * @param constraintAnnotation
     */
    @Override
    public void initialize(ListValue constraintAnnotation) {
       int[] vals = constraintAnnotation.vals();
        //将注解上的校验信息存入Set集合
        if (vals.length != 0){
            for (int val : vals) {
                set.add(val);
            }
        }

    }

    /**
     * 判断是否校验成功
     * @param value  传过来需要校验的值
     * @param context 整个校验的上下文
     * @return
     */
    @Override
    public boolean isValid(Integer value, ConstraintValidatorContext context) {
        //返回是否包含规定的值
        return set.contains(value);
    }
}

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

给校验注解添加 即自定义校验器
可以使用多个校验器,例如需要校验的类型不同时
@Constraint(validatedBy = { ListValueConstraintValidator.class })

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值