美文网首页
spring boot 统一异常处理+@Valid 验证 (二)

spring boot 统一异常处理+@Valid 验证 (二)

作者: 东本三月 | 来源:发表于2019-10-11 18:05 被阅读0次

1.使用环境

  • spring boot 2.1.9 +mybatis-plus 3.2.0

2.增加异常类

  • 在项目添加一些异常类,实现对项目抛出异常的"合并".
  • 这里我把所有异常分为两种,一是业务提示性的异常,如 ""用户名不能少于6位,"已审核的数据不能再次审核" 等等.这些我用ServiceException类进行展示. 二是项目出错性的异常,如 空指针,类型转换异常等,我用ErrorException类进行保存.
  • 用一个父类BasicException来让两个异常类进行继承,方便扩展

BasicException

/**
 * 所有自定义异常的父类
 * @time 2018年9月26日10:53:05
 * @author authstr
 */
public class BasicException extends RuntimeException {

    public final static  String DEFAULT_CODE=BaseExceptionEnum.SERVER_ERROR.getCode();
    String code;
    Object data;
    public BasicException() {
        super();
    }

    public BasicException(String message) {
        super(message);
        code=DEFAULT_CODE;
    }

    public BasicException(String code, String message){
        this(message);
        this.code=code;
    }
    public BasicException(String code, String message, Object data){
        this(message);
        this.code=code;
        this.data=data;
    }

    /**
     * 通过消息枚举创建异常
     * @param msgEnum
     */
    public BasicException(ExceptionEnumInterface msgEnum){
        this(msgEnum.getCode(),msgEnum.getMessage());
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public Object getData() {
        return data;
    }

    public void setData(Object data) {
        this.data = data;
    }


ServiceException

/**
 * 消息型异常,用来在接口返回值显示消息
 * @time 2018年9月26日10:52:51
 * @author authstr
 *
 */
public class ServiceException extends BasicException {
    public ServiceException() {
        super();
    }

    public ServiceException(String message) {
        super(message);
        code=DEFAULT_CODE;
    }
    
    public ServiceException(String code, String message){
        this(message);
        this.code=code;
    }
    public ServiceException(String code, String message, Object data){
        this(message);
        this.code=code;
        this.data=data;
    }
}

ErrorException

/**
 * 错误型异常,用来记录系统内部未知的出错
 * @time 2018年9月26日10:53:05
 * @author authstr
 */
public class ErrorException extends BasicException {
    public ErrorException() {
        super();
    }

    public ErrorException(String message) {
        super(message);
        this.code=DEFAULT_CODE;
    }
    
    public ErrorException(String code, String message){
        this(message);
        this.code=code;
    }
    public ErrorException(String code, String message, Object data){
        this(message);
        this.code=code;
        this.data=data;
    }
}

3.添加提示信息枚举

  • 一般来说错误提示都要有错误代码和错误信息,这些信息用枚举进行统一储存比较方便.
  • 实现比较简单,创建接口ExceptionEnumInterface作为所有信息枚举的抽象,再添加BaseExceptionEnum,实现接口,记录一些基础的常用的提示信息.
  • 后续其他模块可以在本模块增加枚举类,来保存和使用所需要的提示信息

ExceptionEnumInterface

/**
 * 异常消息枚举的实现接口
 */
public interface ExceptionEnumInterface {
     String getCode();
     String getMessage();
}

BaseExceptionEnum

public enum BaseExceptionEnum implements ExceptionEnumInterface {

    SUCCESS("0","操作成功"),
    SERVER_ERROR("500", "服务器异常"),
    PARA_ERROR("400", "请求参数错误"),
    UNKNOWN_ERROR("-1", "未知异常");

    private String code;
    private String message;
    BaseExceptionEnum(String code, String message){
        this.code=code;
        this.message=message;
    }


    @Override
    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    @Override
    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}

4. 提供异常的便捷抛出

  • 一般情况,都是通过if判断,然后抛出异常并设置异常的属性,较麻烦.可以用一个类Assert来减少代码量
    Assert
/**
 * 用于快捷的抛出异常
 * @author authstr
 */
public class Assert {
     /**
      * 为false时抛出一个"服务器错误"的异常
     * @param exp 为false抛出异常
     * @time 2018年9月17日10:10:33
     * @author authstr
     */
    public static void isTrue(boolean exp) {
            Assert.isTrue(exp,BaseExceptionEnum.SERVER_ERROR.getMessage());
        }

    /**
     *  为false时抛出一个指定message的异常
     * @param exp   为false抛出异常
     * @param message 该异常要显示的信息
     * @time 2018年9月17日10:13:05
     * @author authstr
     */
    public static void isTrue(boolean exp, String message) {
        Assert.isTrue(exp, message, false);
    }

    /**
     *  为false时抛出一个指定枚举内信息的异常
     * @param exp   为false抛出异常
     * @param msgEnum 该异常要使用的异常信息枚举
     * @time 2019年10月11日16:34:08
     * @author authstr
     */
    public static void isTrue(boolean exp, ExceptionEnumInterface msgEnum) {
        Assert.isTrue(exp, msgEnum.getCode(), msgEnum.getMessage());
    }

    public static void isTrue(boolean exp, String code,String message) {
        Assert.isTrue(exp, code, message,null,false);
    }

    /**
     * 为false时抛出一个指定message的消息异常或者错误异常
     * @param exp 为false抛出异常
     * @param message 该异常要显示的信息
     * @param isError 该异常是否为错误异常
     * @time 2018年9月17日10:13:17
     * @author authstr
     */
    public static void isTrue(boolean exp, String message, boolean isError) {
         Assert.isTrue(exp, BaseExceptionEnum.SERVER_ERROR.getCode(), message,null,isError);
    }


    /**
     * 为false时抛出一个指定信息的消息异常或者错误异常
     * @param exp 为false抛出异常
     * @param message 异常的详细说明
     * @param data  该异常要附带的数据信息
     * @param isError 该异常是否为错误异常
     * @time 2018年9月26日10:11:19
     * @author authstr
     */
    public static void isTrue(boolean exp, String code, String message,Object data,boolean isError){
         if (!exp) {
            if (!isError) {
                throw new ServiceException(code,message,data);
            }else{
                throw new ErrorException(code,message,data);
            }
         }
    }

    /**
     * 对象为空时,抛出一个异常
     * @param o
     * @time 2019年3月21日20:07:56
     */
    public static void notNull(Object o){
        if(o==null){
            throw new ErrorException(BaseExceptionEnum.PARA_ERROR.getMessage());
        }
    }

5.增加response数据封装类

  • 对请求返回的数据与相关信息进行封装,来规范请求返回的格式,减少代码量
  • 由三个类组成,一是SuccessResponseData,表示成功的请求响应,二是ErrorResponseData,表示出错的请求响应,三是ResponseData,作为之前两个类的父类

ResponseData

import org.springframework.util.StringUtils;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.PropertyUtils;
import java.util.HashMap;
import java.util.Map;

/**
 * 对请求返回的数据进行封装  父类
 * @author authstr
 * @time 2019年10月11日14:10:28
 */
public class ResponseData {
    private Boolean success;
    private String code;
    private String message;
    private Object data;

    public ResponseData(){}

    public ResponseData(Boolean success,String code,String message,Object data){
        this.success = success;
        this.code = code;
        this.message = message;
        this.data = data;
    }

    /**
     * 将枚举的信息添加到对象
     * @param msgenum
     */
    public void addEnumInfo( ExceptionEnumInterface msgenum ){
        setCode(msgenum.getCode());
        setMessage(msgenum.getMessage());
    }

    /**
     * 将异常的信息添加到对象
     * @param e
     */
    public void addExceptionInfo( BasicException e ){
        setSuccess(false);
        if(StringUtils.hasText(e.getCode())){
            setCode(e.getCode());
        }
        if(StringUtils.hasText(e.getMessage())){
            setMessage(e.getMessage());
        }
        if(e.getData()!=null){
            setData(e.getData());
        }
        //获取异常的行号信息 进行储存
        //该功能后续再补充
    }

    public static SuccessResponseData success() {
        return new SuccessResponseData();
    }

    public static SuccessResponseData success(Object object) {
        return new SuccessResponseData(object);
    }


    public static SuccessResponseData success(String code, String message, Object object) {
        return new SuccessResponseData(code, message, object);
    }

    public static ErrorResponseData error(String message) {
        return new ErrorResponseData(message);
    }

    public static ErrorResponseData error(String code, String message) {
        return new ErrorResponseData(code, message);
    }

    public static ErrorResponseData error(String code, String message, Object object) {
        return new ErrorResponseData(code, message, object);
    }

    /**
     * 创建一个默认的成功返回对象.之后以键值对的方式,保存两个对象到Date
     * @param key1 对象1的key
     * @param value1 对象1
     * @param key2 对象2的key
     * @param value2 对象2
     */
    public static SuccessResponseData success(String key1,Object value1,String key2,Object value2) {
        SuccessResponseData successResponseData=new SuccessResponseData();
        successResponseData.setData( key1, value1, key2, value2);
        return successResponseData;
    }

    /**
     * 创建一个默认的成功返回对象.之后以父子关系的方式,保存两个对象到Date.父对象将转换为map作为data,子对象将作为map的一个键值对
     * @param parentData 父对象
     * @param childName 子对象的key
     * @param childData 子对象
     */
    public static SuccessResponseData success(Object parentData,String childName,Object childData) {
        SuccessResponseData successResponseData=new SuccessResponseData();
        successResponseData.setParentChildData( parentData, childName, childData);
        return successResponseData;
    }

   /**
     * 用于以键值对的方式,保存两个对象到Date
     * @param key1 对象1的key
     * @param value1 对象1
     * @param key2 对象2的key
     * @param value2 对象2
     */
    public void setData(String key1,Object value1,String key2,Object value2) {
        putData(key1,value1);
        putData(key2,value2);
    }


    /**
     * 用于以父子关系的方式,保存两个对象到Date.父对象将转换为map作为data,子对象将作为map的一个键值对
     * @param parentData 父对象
     * @param childName 子对象的key
     * @param childData 子对象
     */
    public void setParentChildData(Object parentData,String childName,Object childData) {
        putData(parentData);
        putData(childName,childData);
    }

    /**
     * 将字段data设置为Map对象,原来的对象会转换为Map进行保存,无法转换则保存到'data'键
     */
    public Map<String,Object> dataSetMap(){
        Object old_object=this.getData();
        //判断当前data储存的是否为Map
        if(!(old_object instanceof Map)){
            Map<String,Object> mapData=null;
            //如果不是map,创建一个map, 原来的对象会转换为Map进行保存,无法转换保存到'data'键
            mapData=new HashMap<>();
            if(old_object!=null){
                try {
                    Map<String,Object> tmep=PropertyUtils.describe(old_object);
                    //移除无关的class键
                    tmep.remove("class");
                    mapData.putAll(tmep);
                }catch (Exception e){
                    e.printStackTrace();
                    mapData.put("data",old_object);
                }
            }
            setData(mapData);
        }
        return (Map<String,Object> )getData();
    }


    /**
     * 将一个键值对保存到 data的 map 里
     * @param key
     * @param value
     */
    public void putData(String key,Object value){
        Map<String,Object> mapData=dataSetMap();
        mapData.put(key,value);
    }

    /**
     * 将一个对象转换为map,合并到 data的map 里
     * @param entity
     */
    public void putData(Object entity){
        Map<String,Object> mapData=dataSetMap();
        if(entity!=null){
            try {
                Map<String,Object> tmep= PropertyUtils.describe(entity);
                //移除无关的class键
                tmep.remove("class");
                mapData.putAll(tmep);
            }catch (Exception e){
                e.printStackTrace();
                mapData.put(entity.getClass().getName(),entity);
            }
        }
    }

    public Boolean getSuccess() {
        return success;
    }
    public void setSuccess(Boolean success) {
        this.success = success;
    }
    public String getCode() {
        return code;
    }
    public void setCode(String code) {
        this.code = code;
    }
    public String getMessage() {
        return message;
    }
    public void setMessage(String message) {
        this.message = message;
    }
    public Object getData() {
        return data;
    }
    public void setData(Object data) {
        this.data = data;
    }
}

SuccessResponseData

/**
 * 对请求成功 要返回的数据进行封装
 * @author authstr
 * @time 2019年10月11日14:11:10
 */
public class SuccessResponseData extends ResponseData {
    public SuccessResponseData() {
        super(true, BaseExceptionEnum.SUCCESS.getCode(), BaseExceptionEnum.SUCCESS.getMessage(), (Object)null);
    }
    public SuccessResponseData(Object object) {
        super(true,  BaseExceptionEnum.SUCCESS.getCode(),BaseExceptionEnum.SUCCESS.getMessage(), object);
    }
    public SuccessResponseData(String code, String message, Object object) {
        super(true, code, message, object);
    }
}

ErrorResponseData

/**
 * 对请求失败 要返回的数据进行封装
 * @author authstr
 * @time 2019年10月11日14:12:23
 */
public class ErrorResponseData extends ResponseData {
    private String exceptionClazz;

    public ErrorResponseData() {
        super(false, BaseExceptionEnum.SERVER_ERROR.getCode(), BaseExceptionEnum.SERVER_ERROR.getMessage(), null);
    }

    public ErrorResponseData(String message) {
        super(false, BaseExceptionEnum.SERVER_ERROR.getCode(), message, null);
    }

    public ErrorResponseData(String code, String message) {
        super(false, code, message, null);
    }

    public ErrorResponseData(String code, String message, Object object) {
        super(false, code, message, object);
    }

    public String getExceptionClazz() {
        return exceptionClazz;
    }

    public void setExceptionClazz(String exceptionClazz) {
        this.exceptionClazz = exceptionClazz;
    }
}

6.在model添加@Vaild的相关注解,并进行验证

  • 以user为例
@Data
@TableName(value = "base_user")
public class BaseUser  extends BaseModel {

    //用户名
    @NotEmpty(message = "用户名不能为空")
    @Length(min=6,max = 16,message = "用户名需为6-16字符")
    private String username;

    //密码
    @NotEmpty(message = "密码不能为空")
    private String password;

    //  性别
    private String sex_id;

    //邮箱
    private String email;

    //手机号
    private String phone;

    //备注
    private String remark;

    //状态
    private Integer status_id;
}
  • controller使用@Valid注解
    @RequestMapping("add")
    public ResponseData add(@Valid  BaseUser user) {
        userService.add(user);
        return ResponseData.success();
    }

7. 对所有异常进行捕获处理

/**
 * 控制层的超类,
 * 主要对控制层的异常进行捕获,通过接口返回或者进行其他处理
 * @author authstr
 * 2019年10月7日18:16:41
 */
@Component
public class AbstractController {

    protected Logger log = LoggerFactory.getLogger(this.getClass());

    /**
     * 捕获其他异常
     * @param ex
     * @return
     */
    @ExceptionHandler(value={Exception.class})
    @ResponseBody
    public ErrorResponseData exceptionHandler(Exception ex) {
        ErrorResponseData res=new ErrorResponseData();
        //不确定的异常,在返回值显示未知异常,具体错误消息进行记录和打印
        res.addEnumInfo(BaseExceptionEnum.UNKNOWN_ERROR);
        ex.printStackTrace();
        this.log.error( "系统出现未知异常 :" + ex.getMessage());
        return res;
    }

    /**
     * 捕获消息型的异常
     * @param ex
     * @return
     */
    @ExceptionHandler(value={ServiceException.class})
    @ResponseBody
    public ErrorResponseData exceptionHandler(ServiceException ex) {
        ErrorResponseData res=new ErrorResponseData();
        res.addExceptionInfo(ex);
        return res;
    }

    /**
     * 捕获出错型的异常
     * @param ex
     * @return
     */
    @ExceptionHandler(value={ErrorException.class})
    @ResponseBody
    public ErrorResponseData exceptionHandler(ErrorException ex) {
        ErrorResponseData res=new ErrorResponseData();
        //出现错误型异常,在返回值显示服务器异常,具体错误消息进行记录和打印
        ex.printStackTrace();
        log.error("系统执行出现异常:" + ex.getMessage());
        return res;
    }

    /**
     * 捕获数据绑定异常
     * @param ex
     * @return
     */
    @ExceptionHandler(value={BindException.class,MethodArgumentNotValidException.class})
    @ResponseBody
    public ErrorResponseData bindExceptionHandler(Exception ex) {
        ErrorResponseData res=new ErrorResponseData();
        //出现参数不正确的异常,在返回值显示提示信息,具体错误消息进行记录和打印
        //设置为参数错误
        res.addEnumInfo(BaseExceptionEnum.PARA_ERROR);
        List<FieldError> fieldErrors=null;
        if(ex instanceof BindException){
            fieldErrors=((BindException)ex).getBindingResult().getFieldErrors();
        }else if(ex instanceof  MethodArgumentNotValidException){
            fieldErrors=((MethodArgumentNotValidException)ex).getBindingResult().getFieldErrors();
        }else{
            return res;
        }
        List<String> allError=new ArrayList<>();
        //将第一个错误信息作为响应的错误信息
        res.setMessage(fieldErrors.get(0).getDefaultMessage());
        //记录其他错误信息
        for (FieldError error:fieldErrors){
            allError.add(error.getDefaultMessage());
        }
        res.setData(allError);
        return res;
    }
}

相关文章

网友评论

      本文标题:spring boot 统一异常处理+@Valid 验证 (二)

      本文链接:https://www.haomeiwen.com/subject/vjlxmctx.html