SpringBoot優雅的全局異常處理(二)——@RestControllerAdvice@ExceptionHandler方式

前言

SpringBoot版本:2.1.9.RELEASE
Mybatis Plus版本:3.3.0

上篇文章主要是講的SpringBoot非web項目的全局異常處理方式,現在來講一下web註解的方式。

一、新建自定義異常處理類GlobalExceptionHandler。

package com.junya.util.exception;

import com.junya.util.result.ResponseMessage;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.servlet.http.HttpServletRequest;

/**
 * @program sweet-dream
 * @description: 自定義全局異常處理類
 * @author: zhangchao
 * @date: 2020/02/26 22:05
 * @since: 1.0.0
 */
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

    /**
     * 全局異常攔截處理
     * @param request
     * @param e
     * @return
     */
    @ExceptionHandler
    public ResponseMessage exceptionHandler(HttpServletRequest request, Exception e){
        String requestUrl = request.getRequestURL().toString();//得到請求的URL地址
        String queryString = request.getQueryString();//得到請求的URL地址中附帶的參數
        String remoteAddr = request.getRemoteAddr();//得到來訪者的IP地址
        String method = request.getMethod();//得到請求URL地址時使用的方法
        //異常堆棧信息放入日誌中
        String api = requestUrl.replaceFirst("[\\s\\S]*\\d(.+)","$1");
        ExceptionUtil.getFullStackTrace(e,api);
        ResponseMessage responseMessage = new ResponseMessage();
        String eStr = e.toString();
        for (GlobalErrorCodeEnum global : GlobalErrorCodeEnum.values()){
            if (eStr.toUpperCase().contains(global.toString().replaceAll("_",""))){
                responseMessage.setCode(global.getCode());
                responseMessage.setMsg(global.getMsg());
                break;
            }
        }
        if (eStr.contains("GlobalException")){
            for (GlobalErrorCodeEnum statusCodeEnum : GlobalErrorCodeEnum.values()){
                if (eStr.contains(statusCodeEnum.getMsg())){
                    if (eStr.contains("Data too long for column")){
                        String field = eStr.replaceAll("[\\s\\S]+Data too long for column '(.+)' at[\\s\\S]+","$1");
                        responseMessage.setCode(statusCodeEnum.getCode());
                        responseMessage.setMsg(statusCodeEnum.getMsg()+",字段長度超過限制:"+field);
                        break;
                    }
                    if (eStr.contains("Unknown column")){
                        String field = eStr.replaceAll("[\\s\\S]+Unknown column '(.+)' in[\\s\\S]+","$1");
                        responseMessage.setCode(statusCodeEnum.getCode());
                        responseMessage.setMsg(statusCodeEnum.getMsg()+",未知的字段:"+field);
                        break;
                    }
                    if (eStr.contains("doesn't have a default value")){
                        String field = eStr.replaceAll("[\\s\\S]+Field '(.+)' doesn't have a default value[\\s\\S]+","$1");
                        responseMessage.setCode(statusCodeEnum.getCode());
                        responseMessage.setMsg(statusCodeEnum.getMsg()+",此字段必須有值:"+field);
                        break;
                    }
                    responseMessage.setCode(statusCodeEnum.getCode());
                    responseMessage.setMsg(statusCodeEnum.getMsg());
                    break;
                }
            }
        }
        if ("".equals(responseMessage.getCode()) || "".equals(responseMessage.getMsg())){
            responseMessage.setCode(GlobalErrorCodeEnum.UNKNOWN_EXCEPTION.getCode());
            responseMessage.setMsg(GlobalErrorCodeEnum.UNKNOWN_EXCEPTION.getMsg());
        }
        responseMessage.setData("{請求PATH:"+api+",請求參數:"+queryString+",來訪者IP:"+remoteAddr+",請求方法類型:"+method+"}");
        return responseMessage;
    }

}

二、 自定義異常類,繼承RuntimeException。

package com.junya.util.exception;

/**
 * 自定義全局異常類
 *
 * @author ZhangChao
 * @date 2019/10/18 13:23
 * @since 1.0.0
 */
public class GlobalException extends RuntimeException {
    private static final long serialVersionUID = 6958499252468627021L;

    /**
     * 錯誤碼
     */
    private String code;

    public GlobalException(String code, String msg) {
        super(msg);
        this.code = code;
    }

    public GlobalException(GlobalErrorCodeEnum errorCode) {
        super(errorCode.getMsg());
        this.code = errorCode.getCode();
    }

    public GlobalException(GlobalErrorCodeEnum errorCode, String msg){
        super(errorCode.getMsg()+msg);
        this.code = errorCode.getCode();
    }

    public GlobalException(String code, String msg, Throwable throwable){
        super(msg,throwable);
        this.code = code;
    }
    public GlobalException(GlobalErrorCodeEnum errorCode, Throwable throwable){
        super(errorCode.getMsg(),throwable);
        this.code = errorCode.getCode();
    }

    public String getCode() {
        return code;
    }

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

三、異常枚舉類

package com.junya.util.exception;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * 自定義全局異常枚舉
 *
 * @author ZhangChao
 * @date 2019/10/18 13:15
 * @since 1.0.0
 */
public enum GlobalErrorCodeEnum {

    /** 未知異常 */
    UNKNOWN_EXCEPTION("CSEP001","未知異常,請聯繫管理員"),
    /** 系統錯誤 */
    SYSTEM_ERROR("CSEP002","系統錯誤"),
    /** 類轉換異常 */
    CLASS_CAST_EXCEPTION("CSEP003","類型強制轉換異常"),
    /** 算術條件異常 */
    ARITHMETIC_EXCEPTION("CSEP004","算術條件異常"),
    /** 空指針異常 */
    NULL_POINTER_EXCEPTION("CSEP005","空指針異常"),
    /** 字符串轉換爲數字異常 */
    NUMBER_FORMAT_EXCEPTION("CSEP006","字符串轉換爲數字異常"),
    /** 數組下標越界異常 */
    ARRAY_INDEX_OUT_OF_BOUNDS_EXCEPTION("CSEP007","數組下標越界異常"),
    /** 方法未找到異常 */
    NO_SUCH_METHOD_EXCEPTION("CSEP008","方法未找到異常"),
    /** 未找到類定義錯誤 */
    NO_CLASS_DEF_FOUND_ERROR("CSEP009","未找到類定義錯誤"),
    /** 未找到類定義錯誤 */
    CLASS_NOT_FOUND_EXCEPTION("CSEP010","找不到類異常"),
    /** 索引越界異常 */
    INDEX_OUT_OF_BOUNDS_EXCEPTION("CSEP011","索引越界異常"),
    /** 數據庫異常 */
    DB_ERROR("CSEP012","數據庫異常")
    ;

    private String code;
    private String msg;

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

    public String getCode() {
        return code;
    }

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

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

}

四、異常工具類

package com.junya.util.exception;

import com.junya.util.result.ResponseMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.StringWriter;

/**
 * 保存異常堆棧信息
 * -- 兩種方式 Throwable和Exception
 *
 * @author ZhangChao
 * @date 2019/9/6 9:56
 * @since 1.0.0
 */
public class ExceptionUtil {

    private static final Logger logger = LoggerFactory.getLogger(ExceptionUtil.class);

    /**
     * 異常堆棧信息保存到日誌中
     * @param ex
     */
    public static void getFullStackTrace(Exception ex) {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        PrintStream pout = new PrintStream(out);
        ex.printStackTrace(pout);
        String ret = new String(out.toByteArray());
        pout.close();
        try {
            out.close();
        } catch (Exception e) {
        }
        ex.printStackTrace();
        logger.error(ret);
    }

    /**
     * 異常堆棧信息保存到日誌中,並保存消息記錄
     * @param ex
     * @param msg
     */
    public static void getFullStackTrace(Exception ex, String msg) {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        PrintStream pout = new PrintStream(out);
        ex.printStackTrace(pout);
        String ret = new String(out.toByteArray());
        pout.close();
        try {
            out.close();
        } catch (Exception e) {
        }
        ex.printStackTrace();
        logger.error("出現異常==>: "+msg+" ==> \n"+ret);
//        logger.error(ret);
    }

    /**
     * 參數是Throwable
     * @param e
     * @return
     */
    public static void getFullStackTrace(Throwable e){
        StringWriter sw = new StringWriter();
        PrintWriter pw = new PrintWriter(sw, true);
        try {
            e.printStackTrace(pw);
            pw.flush();
            sw.flush();
            logger.error(sw.toString());
        } finally {
            pw.close();
        }
    }

}

五、測試
在這裏插入圖片描述

總結

通過@RestControllerAdvice@ExceptionHandler方式還是很簡單的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章