1. 程式人生 > >SpringMVC之全域性異常處理 ——統一返回格式(自定義異常)

SpringMVC之全域性異常處理 ——統一返回格式(自定義異常)

SpringMVC之全域性異常處理

老規矩開篇咱們先介紹一下背景
因當前APP越來越流行,或是提供的第三方介面等等都需要你來統一返回格式。這個時候問題就來了 ,很多時候系統的異常以及為了程式碼的可讀性我們必然會抽出很多的間接層(例如資料格式校驗、資料有效性校驗等),一層層的return是否讓你煩不勝煩?其實只需要丟擲異常就像斷言那樣即可阻止程式繼續執行後續業務程式碼。

Dennis Debruler 說過 “計算機是這樣一門科學:它相信所有的問題都可以通過增加一個間接層來解決。”

首先定義好我們的返回物件

package com.xxx.response.common;


import java.io.Serializable;

public class Response<T> implements Serializable {
	private static final long serialVersionUID = 1L;
	private Integer code;
	private String message;
	private T result;

	public static boolean isSuccess(Response<?> response) {
		return response == null ? false : ResponsCodeTypeEnum.SUCCESS.getCode().equals(response.getCode());
	}

	public Response() {
		this.code = ResponsCodeTypeEnum.SUCCESS.getCode();
		this.message = ResponsCodeTypeEnum.SUCCESS.getMessage();
	}

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

	public Response(T result) {
		this.code = ResponsCodeTypeEnum.SUCCESS.code;
		this.message = ResponsCodeTypeEnum.SUCCESS.message;
		this.result = result;
	}

	public Integer getCode() {
		return this.code;
	}

	public String getMessage() {
		return this.message;
	}

	public T getResult() {
		return this.result;
	}

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

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

	public void setResult(T result) {
		this.result = result;
	}

	public String toString() {
		return "Response(code=" + this.getCode() + ", message=" + this.getMessage() + ", result=" + this.getResult() + ")";
	}
	
	//為了方便將列舉類整合至一起了可以單獨建一個
	public enum ResponsCodeTypeEnum {
		SUCCESS(0, "請求成功"),
		SYSTEM_BUSY(100, "系統繁忙"),
		REQUEST_TIME_OUT(300, "請求超時"),
		PARAMETER_ERROR(400, "引數錯誤"),
		NETWORK_ERROR(404, "網路異常"),
		DATA_NOT_EXISTS(600, "資料不存在"),
		FAILURE(999, "未知錯誤");

		private Integer code;
		private String message;

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

		public Integer getCode() {
			return this.code;
		}

		public String getMessage() {
			return this.message;
		}
	}
}

接下來自定義異常

我這些寫的複雜些可以去除掉你們不需要的

定義介面

package com.xxx.common.exception;

import java.util.Date;

public interface BaseException {
    Integer getCode();

    String[] getArgs();

    void setTime(Date var1);

    Date getTime();

    void setClassName(String var1);

    String getClassName();

    void setMethodName(String var1);

    String getMethodName();

    void setParameters(String[] var1);

    String[] getParameters();

    void setHandled(boolean var1);

    boolean isHandled();

    String getMessage();

    void setI18nMessage(String var1);

    String getI18nMessage();
}

異常工具類

package com.xxx.common.exception.util;

import java.io.PrintWriter;
import java.io.StringWriter;

public class ExceptionUtils extends org.apache.commons.lang3.exception.ExceptionUtils {
    public ExceptionUtils() {
    }

    public static String[] convertArgsToString(Object[] args) {
        String[] argsStrs = new String[args.length];

        for(int i = 0; i < args.length; ++i) {
            argsStrs[i] = String.valueOf(args[i]);
        }

        return argsStrs;
    }

    public static String toString(Throwable e) {
        return toString("", e);
    }

    public static String toString(String msg, Throwable e) {
        StringWriter w = new StringWriter();
        w.write(msg);
        PrintWriter p = new PrintWriter(w);
        p.println();

        String var4;
        try {
            e.printStackTrace(p);
            var4 = w.toString();
        } finally {
            p.close();
        }

        return var4;
    }
}

超類

package com.xxx.common.exception;

import com.xxx.common.exception.util.ExceptionUtils;
import org.springframework.core.NestedRuntimeException;

import java.util.Date;

public class BaseRuntimeException extends NestedRuntimeException implements BaseException {
    private static final long serialVersionUID = 1L;
    private Integer code;
    private Date time;
    private String[] args;
    private String className;
    private String methodName;
    private String[] parameters;
    private boolean handled;
    private String i18nMessage;

    public BaseRuntimeException(Integer code, String defaultMessage, Object[] args) {
        super(defaultMessage);
        this.code = code;
        this.args = ExceptionUtils.convertArgsToString(args);
    }

    public BaseRuntimeException(Integer code, String defaultMessage, Throwable cause, Object[] args) {
        super(defaultMessage, cause);
        this.code = code;
        this.args = ExceptionUtils.convertArgsToString(args);
    }

    public BaseRuntimeException(String defaultMessage, Throwable cause) {
        super(defaultMessage, cause);
    }

    public BaseRuntimeException(String defaultMessage) {
        super(defaultMessage);
    }

    public Integer getCode() {
        return this.code;
    }

    public Date getTime() {
        return this.time;
    }

    public void setTime(Date time) {
        this.time = time;
    }

    public String getClassName() {
        return this.className;
    }

    public void setClassName(String className) {
        this.className = className;
    }

    public String getMethodName() {
        return this.methodName;
    }

    public void setMethodName(String methodName) {
        this.methodName = methodName;
    }

    public String[] getParameters() {
        return this.parameters;
    }

    public void setParameters(String[] parameters) {
        this.parameters = parameters;
    }

    public void setHandled(boolean handled) {
        this.handled = handled;
    }

    public boolean isHandled() {
        return this.handled;
    }

    public void setI18nMessage(String i18nMessage) {
        this.i18nMessage = i18nMessage;
    }

    public String getI18nMessage() {
        return this.i18nMessage;
    }

    public String[] getArgs() {
        return this.args;
    }
}

異常類

package com.xxx.common.exception;

public class FastRuntimeException extends BaseRuntimeException {
    private static final long serialVersionUID = -4954118251735823026L;

    public FastRuntimeException(String msg) {
        super(msg);
    }

    public FastRuntimeException(Integer code, String defaultMsg, Object[] args) {
        super(code, defaultMsg, args);
    }

    public FastRuntimeException(Integer code, String msg) {
        super(code, msg, new Object[0]);
    }

    public FastRuntimeException(String msg, Throwable cause) {
        super(msg, cause);
    }

    public FastRuntimeException(Integer code, String msg, Throwable cause) {
        super(code, msg, cause, new Object[0]);
    }

    public Throwable fillInStackTrace() {
        return this;
    }
}

異常定義完成,可直接使用FastRuntimeException 不過我建議各個系統模組去繼承FastRuntimeException 定義自己的異常

全域性異常捕獲

package com.xxx.common.exception;

import com.alibaba.dubbo.rpc.RpcException;
import com.xxx.common.response.Response;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.validation.ValidationException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;


@ControllerAdvice
@ResponseBody
public class GlobalExceptionHandler {
    private Logger log = LoggerFactory.getLogger(this.getClass());

    public GlobalExceptionHandler() {
    }

    @ExceptionHandler({Exception.class})
    public Response<Map<String, String>> MethodArgumentNotValidHandler(Exception exception) throws Exception {
        Response<Map<String, String>> response = new Response();
        HashMap fieldAndMessage;
        Iterator var5;
        FieldError fieldError;
        if (exception instanceof MethodArgumentNotValidException) {
            fieldAndMessage = new HashMap();
            MethodArgumentNotValidException methodArgumentNotValidException = (MethodArgumentNotValidException)exception;
            var5 = methodArgumentNotValidException.getBindingResult().getFieldErrors().iterator();

            while(var5.hasNext()) {
                fieldError = (FieldError)var5.next();
                fieldAndMessage.put(fieldError.getField(), fieldError.getDefaultMessage());
            }

            response.setCode(Response.ResponsCodeTypeEnum.PARAMETER_ERROR.getCode());
            response.setMessage(Response.ResponsCodeTypeEnum.PARAMETER_ERROR.getMessage());
            response.setResult(fieldAndMessage);
        } else if (exception instanceof BindException) {
            fieldAndMessage = new HashMap();
            BindException bindException = (BindException)exception;
            var5 = bindException.getBindingResult().getFieldErrors().iterator();

            while(var5.hasNext()) {
                fieldError = (FieldError)var5.next();
                fieldAndMessage.put(fieldError.getField(), fieldError.getDefaultMessage());
            }

            response.setCode(Response.ResponsCodeTypeEnum.PARAMETER_ERROR.getCode());
            response.setMessage(Response.ResponsCodeTypeEnum.PARAMETER_ERROR.getMessage());
            response.setResult(fieldAndMessage);
        } else if (exception instanceof ValidationException) {
            response.setCode(Response.ResponsCodeTypeEnum.PARAMETER_ERROR.getCode());
            response.setMessage(Response.ResponsCodeTypeEnum.PARAMETER_ERROR.getMessage());
        } else if (exception instanceof RpcException) {
            response.setCode(Response.ResponsCodeTypeEnum.PARAMETER_ERROR.getCode());
            response.setMessage(Response.ResponsCodeTypeEnum.PARAMETER_ERROR.getMessage());
        } else if (exception instanceof BaseRuntimeException) {
        	//取出我們放入異常中的code 和message 返回前端
            response.setCode(((BaseRuntimeException) exception).getCode());
            response.setMessage(exception.getMessage());
        }else {
            response.setCode(Response.ResponsCodeTypeEnum.FAILURE.getCode());
            response.setMessage(Response.ResponsCodeTypeEnum.FAILURE.getMessage());
        }
        this.log.error(exception.getMessage(), exception);
        return response;
    }
}

打完收工,一切的程式碼都是可以不做任何修改直接拿去使用的。大大的方便了我們的業務開發。接下來請讓你的校驗工作變成間接層吧。

我的簡化版登入校驗工廠
在這裡插入圖片描述