优雅的处理API接口开发时的全局异常

对于后端程序员来说,写Java程序的时候, 处理异常是必须要做的事,也是一个系统最重要的环节,当一个项目变得很大的时候,异常处理和日志系统能让你快速定位到问题。对于用户或者接口调用者而言,优雅的异常处理可以让调用者快速知道问题所在。本文将介绍如何优雅地处理异常。

‍♀️异常枚举类

枚举类,方便统一管理异常CODE和异常信息

/**
 * @author : 小影
 * @date : 2021/1/20 17:45
 * @description:
 */
public enum ErrCodeEnum {

   // 请求方式错误,请使用GET|POST
   unknown_405(405, "Request mode error"),
   // 内部服务器错误
   unknown_500(500, "Internal server error"),
   // 未知异常,请联系技术工程师
   unknown_9999(9999, "If the fault is unknown, contact technical support"),
   // 网络异常,稍候重试
   unknown_5555(5555, "Network exception, please try again later"),
   // 请求参数异常
   unknown_10000(10000, "Request parameter exception"),
   // 请求正文丢失
   unknown_10001(10001, "Required request body is missing"),

   unknown_10002(10002, "待拓展"),
   unknown_10003(10003, "待拓展"),
   unknown_10004(10004, "待拓展"),
   unknown_10005(10005, "待拓展"),
   unknown_10006(10006, "待拓展"),
   unknown_10007(10007, "待拓展"),
   unknown_10008(10008, "待拓展"),
   unknown_10009(10009, "待拓展"),
   unknown_10010(10010, "待拓展"),
   unknown_10011(10011, "待拓展"),
   unknown_10012(10012, "待拓展"),
   unknown_10013(10013, "待拓展"),
   unknown_10014(10014, "待拓展"),
   unknown_10015(10015, "待拓展"),
   unknown_10016(10016, "待拓展"),
   unknown_10017(10017, "待拓展"),
   unknown_10018(10018, "待拓展"),
   unknown_10019(10019, "待拓展"),
   unknown_10020(10020, "待拓展"),
   unknown_10021(10021, "待拓展"),
   unknown_10022(10022, "待拓展"),
   unknown_10023(10023, "待拓展"),
   unknown_10024(10024, "待拓展"),
   unknown_10025(10025, "待拓展");

   private Integer code;
   private String message;

   ErrCodeEnum(Integer code,
               String message) {
      this.code = code;
      this.message = message;
   }

   public Integer getCode() {
      return code;
   }

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

   public String getMessage() {
      return message;
   }

   public void setMessage(String message) {
      this.message = message;
   }
   @Override
   public String toString() {
      return "{" +
              "code:" + code +
              ", message:'" + message + '\'' +
              '}';
   }
}

自定义断言

优雅的 Assert(断言) 可以用来校验业务的异常情况,消除 if else,将判断方法封装,直接使用.方法名的方法,把会出现的业务异常尽可能的封装到一个类中。简化控制层。方便以后扩展用

import org.springframework.lang.Nullable;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

/**
 * @author 小影
 * @create 2022-04-24 9:24
 * @describe:断言
 */
public abstract class Assert {
   /**
    * 如果条件为true就抛异常
    * @param expression
    * @param message
    */
   public static void isTrue(boolean expression, ErrCodeEnum message) {
      if (expression) {
         throw new IllegalArgumentException(message.toString());
      }
   }

   /**
    * 小于等于0
    * @param number
    * @param message
    */
   public static void isGreater(int number, ErrCodeEnum message) {
      if (number <= 0) {
         throw new IllegalArgumentException(message.toString());
      }
   }

   /**
    * 空对象
    * @param obj
    * @param message
    */
   public static void isEmpty(Object obj, ErrCodeEnum message) {
      if (ObjectUtils.isEmpty(obj)) {
         throw new IllegalArgumentException(message.toString());
      }
   }

   /**
    * 空字符
    * @param message
    * @param text
    */
   public static void isStr(ErrCodeEnum message, @Nullable String... text) {
      for (String s : text) {
         if (!StringUtils.hasText(s)) {
            throw new IllegalArgumentException(message.toString());
         }
      }
   }

   /**
    * 字符串是否相等
    * @param str1
    * @param str2
    * @param message
    */
   public static void isStrEq(String str1, String str2, ErrCodeEnum message) {
      if (!str1.equals(str2)) {
         throw new IllegalArgumentException(message.toString());
      }

   }

   /**
    * 如果条件为false 就抛异常
    * @param expression
    * @param message
    */
   public static void isFalse(boolean expression, ErrCodeEnum message) {
      if (!expression) {
         throw new IllegalArgumentException(message.toString());
      }
   }
}

结果集封装

将响应给用户的所有的错误都以Json的方式返回,因此封装一个通用的返回体

import lombok.Data;

@Data
public class ResultUtils {
    //状态码:200成功 100失败
    private int code;
    //成功或失败的提示信息
    private String msg;
    //用户要返回给浏览器的数据
    private T data;

    //成功调用的方法
    public static ResultUtils success(){
        ResultUtils mag = new ResultUtils();
        mag.setCode(200);
        mag.setMsg("成功!");
        return  mag;
    }
    //成功调用的方法
    public static ResultUtils success(T data) {
        ResultUtils entity = new ResultUtils();
        entity.setData(data);
        entity.setMsg("成功!");
        entity.setCode(200);
        return entity;
    }


    //失败调用的方法
    public static ResultUtils error(ErrCodeEnum errCodeEnum) {
        ResultUtils entity = new ResultUtils();
        entity.setCode(errCodeEnum.getCode());
        entity.setMsg(errCodeEnum.getMessage());
        return entity;
    }

    //失败调用的方法
    public static ResultUtils error(ErrCodeEnum errCodeEnum,String msg) {
        ResultUtils entity = new ResultUtils();
        entity.setCode(errCodeEnum.getCode());
        entity.setMsg(msg);
        return entity;
    }
     //失败调用的方法
    public static ResultUtils error(Integer code, String message) {
        ResultUtils entity = new ResultUtils();
        entity.setCode(code);
        entity.setMsg(message);
        return entity;
    }
    //失败调用的方法
    public static ResultUtils error(ErrCodeEnum errCodeEnum,String msg,T obj) {
        ResultUtils entity = new ResultUtils();
        entity.setCode(errCodeEnum.getCode());
        entity.setMsg(msg);
        entity.setData(obj);
        return entity;
    }
    //失败调用的方法
    public static ResultUtils error(ErrCodeEnum errCodeEnum,T obj) {
        ResultUtils entity = new ResultUtils();
        entity.setCode(errCodeEnum.getCode());
        entity.setData(obj);
        return entity;
    }
}

全局异常拦截器

将异常进行统一处理,减少try-catch模板代码,减少编码量,提升扩展性和可维护性。

import com.alibaba.fastjson.JSONObject;
import com.ying.demo.utils.ErrCodeEnum;
import com.ying.demo.utils.ResultUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.servlet.http.HttpServletRequest;

/**
 * @author : 小影
 * @date : 2021/1/20 17:45
 * @description:
 */
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

    /**
     * 请求体丢失
     * @param request
     * @param re
     * @return
     */
    @ExceptionHandler(HttpMessageNotReadableException.class)
    @ResponseStatus(HttpStatus.OK)
    public ResultUtils httpMessageNotReadableException(HttpServletRequest request, HttpMessageNotReadableException re) {
        log.error("[GlobalExceptionHandler]:"+re.getMessage());
        return ResultUtils.error(ErrCodeEnum.unknown_10001, "Required request body is missing");
    }

    /**
     * JSR303 校验拦截
     * 如果抛出的是参数异常,将其当做业务异常处理
     * 参数校验不通过时,抛出以下异常
     * @param request
     * @param re
     * @return
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseStatus(HttpStatus.OK)
    public ResultUtils handlerMethodArgumentNotValid(HttpServletRequest request, MethodArgumentNotValidException re) {
        StringBuilder sb = new StringBuilder();
        re.getBindingResult().getAllErrors().forEach(error -> {
            String fieldName = ((FieldError) error).getField();
            sb.append("[").append(fieldName).append("] ").append(error.getDefaultMessage());
        });
        log.info("Parameter check problem:{}",sb);
        return ResultUtils.error(ErrCodeEnum.unknown_10000, sb.toString());
    }

    /**
     * 请求方式错误拦截
     * @param request
     * @param e
     * @return
     */
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    @ResponseStatus(HttpStatus.OK)
    public ResultUtils handlerHttpRequestMethodNotSupportedException(HttpServletRequest request, HttpRequestMethodNotSupportedException e) {
        return ResultUtils.error(ErrCodeEnum.unknown_405,e.getMessage());
    }

    /**
     * 断言异常拦截
     * @param request
     * @param e
     * @return
     */
    @ExceptionHandler(IllegalArgumentException.class)
    @ResponseStatus(HttpStatus.OK)
      public ResultUtils illegalArgumentException(HttpServletRequest request, IllegalArgumentException e) {
        log.error("Assertion Vs Exception:{}", e.getMessage());
        JSONObject error = JSONObject.parseObject(e.getMessage());
        return ResultUtils.error(error.getInteger("code"), error.getString("message"));
    }

    /**
     * 其他异常拦截
     * @param e
     * @param request
     * @return
     */
    @ExceptionHandler(value = Exception.class)
    @ResponseBody
    public ResultUtils handleException(Exception e, HttpServletRequest request) {
        log.error("Global Exception Occured => url : {}, msg : {}", request.getRequestURL(), e.getMessage());
        e.printStackTrace();
        return ResultUtils.error(ErrCodeEnum.unknown_500);
    }
}

测试

优雅的处理API接口开发时的全局异常_第1张图片
优雅的处理API接口开发时的全局异常_第2张图片

你可能感兴趣的:(笔记&小案例,java)