目標:通過ControllerAdvice和ExceptionHandler捕獲異常和錯誤信息,向前端返回json格式的狀態碼及異常描述信息。
1.創建一個全局異常捕獲處理類GlobalExceptionHandler
package com.qiqi.exception;
import com.qiqi.utils.ExceptionUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.TypeMismatchException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* 全局異常捕獲處理
*/
@ControllerAdvice
public class GlobalExceptionHandler {
private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
/**
* http請求的方法不正確
*/
@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
@ResponseBody
public String httpRequestMethodNotSupportedExceptionHandler(HttpRequestMethodNotSupportedException e) {
logger.error("http請求的方法不正確:【"+e.getMessage()+"】");
return ExceptionUtil.resultOf(ResultStatusCode.RequestMethodNotAllowed);
}
/**
* 請求參數不全
*/
@ExceptionHandler(MissingServletRequestParameterException.class)
@ResponseBody
public String missingServletRequestParameterExceptionHandler(MissingServletRequestParameterException e) {
logger.error("請求參數不全:【"+e.getMessage()+"】");
return ExceptionUtil.resultOf(ResultStatusCode.MissingServletRequestParameter);
}
/**
* 請求參數類型不正確
*/
@ExceptionHandler(TypeMismatchException.class)
@ResponseBody
public String typeMismatchExceptionHandler(TypeMismatchException e) {
logger.error("請求參數類型不正確:【"+e.getMessage()+"】");
return ExceptionUtil.resultOf(ResultStatusCode.TypeMismatchException);
}
/**
* 數據格式不正確
*/
@ExceptionHandler(DataFormatException.class)
@ResponseBody
public String dataFormatExceptionHandler(DataFormatException e) {
logger.error("數據格式不正確:【"+e.getMessage()+"】");
return ExceptionUtil.resultOf(ResultStatusCode.DataFormatException);
}
/**
* 用戶沒找到
*/
@ExceptionHandler(UserNotFoundException.class)
@ResponseBody
public String userNotFoundExceptionHandler(UserNotFoundException e) {
logger.error("用戶沒找到:【"+e.getMessage()+"】");
return ExceptionUtil.resultOf(ResultStatusCode.UserNotExist);
}
/**
* 非法輸入
*/
@ExceptionHandler(IllegalArgumentException.class)
@ResponseBody
public String illegalArgumentExceptionHandler(IllegalArgumentException e) {
logger.error("非法輸入:【"+e.getMessage()+"】");
return ExceptionUtil.resultOf(ResultStatusCode.IllegalArgumentException);
}
@ExceptionHandler //處理其他異常
@ResponseBody
public String allExceptionHandler(Exception e){
logger.error("具體錯誤信息:【"+ExceptionUtil.getErrorMessage(e)+"】"); //會記錄出錯的代碼行等具體信息
int count = 0; //只打印15行的錯誤堆棧
for (StackTraceElement stackTraceElement : e.getStackTrace()) {
logger.error(stackTraceElement.toString());
if(count++ > 13) break;
}
return ExceptionUtil.resultOf(ResultStatusCode.SystemException);
}
}
2.自定義了一些異常UserNotFoundException、DataFormatException等。
public class UserNotFoundException extends Exception {
public UserNotFoundException() {
}
public UserNotFoundException(String message) {
super(message);
}
public UserNotFoundException(String message, Throwable cause) {
super(message, cause);
}
}
3.自定義一個異常狀態碼的枚舉類 ResultStatusCode
public enum ResultStatusCode {
Success("0", "Success"),
UserNotExist("1", "User not exist"),
InValidParameter("2","Invalid parameter"),
DataFormatException("4", "DataFormat exception"),
DataNotExistException("5", "DataNotExistException"),
TimeFormatException("6","TimeFormat Exception"),
PictureFormatException("7","PictureFormat Exception"),
IllegalArgumentException("8","IllegalArgumentException"),
TokenInvalidOrOverdueException("9", "Token invalid or overdue exception"),
AuthorizationCodeError("10", "authorization code error"),
WrongSignatureException("11","Wrong Signature Exception"),
SystemException("50", "system Exception"),
MissingServletRequestParameter("400","Missing servletRequest parameter"),
TypeMismatchException("401","Request parameter Type not match"),
RequestMethodNotAllowed("405","Request method not Allowed"),
;
private String code;
private String msg;
private ResultStatusCode(String code,String msg){
this.code=code;
this.msg=msg;
}
public String getMsg(){
return this.msg;
}
public String getCode(){
return this.code;
}
}
4.自定義異常處理工具類
裏面編寫兩個方法:
方法一:打印異常的詳細信息
方法二:將枚舉類型的狀態碼及描述轉爲json
注意:fastjson包,必須爲1.2.24,否則沒有configEnumAsJavaBean()方法。
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializeConfig;
import com.qiqi.exception.ResultStatusCode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
/**
* 返回異常的處理
*/
public class ExceptionUtil {
private static final Logger logger = LoggerFactory.getLogger(ExceptionUtil.class);
private ExceptionUtil() {
}
/**
* 獲取異常信息
*
* @param e 異常
*/
public static String getErrorMessage(Exception e) {
StringWriter sw = null;
PrintWriter pw = null;
try {
sw = new StringWriter();
pw = new PrintWriter(sw);
// 將出錯的棧信息輸出到printWriter中
e.printStackTrace(pw);
pw.flush();
sw.flush();
} finally {
if (sw != null) {
try {
sw.close();
} catch (IOException e1) {
logger.info(e1.getMessage());
}
}
if (pw != null) {
pw.close();
}
}
return sw.toString();
}
/**
* 異常信息-->json
*
*/
public static String resultOf(ResultStatusCode resultStatusCode) {
SerializeConfig config = new SerializeConfig();
config.configEnumAsJavaBean(ResultStatusCode.class);
return JSON.toJSONString(resultStatusCode, config);
}
}
5.使用
Controller層:拋出異常讓GlobalExceptionHandler捕獲處理
/**
* 刪除用戶
*/
@RequestMapping(value = "/delete", method = RequestMethod.GET)
public Response deleteUser(@RequestParam(value = "user_id", required = true) String userId)
throws UserNotFoundException {
Response response = new Response();
long id = Long.parseLong(userId);
userService.deleteUser(id);
response = new Response("0","刪除用戶成功",null);
logger.info("刪除用戶:id="+ userId + " result="+response.toString());
return response;
}
Service層: 拋出異常
public void deleteUser(long userId )throws UserNotFoundException {
if (userRepository.existsById(userId) == false){
throw new UserNotFoundException();
}
userRepository.deleteById(userId);
}
6.測試結果
一:填寫請求參數時,故意讓String類型的user_id =“3a",在long id = Long.parseLong(userId);轉換時,會拋出IllegalArgumentException異常的子類NumberFormatException。由於我們在GlobalExceptionHandler捕獲處理了IllegalArgumentException這個非法輸入異常,並對其定義了狀態碼及描述。所以前端會收到json格式的響應:
{"code":"8","msg":"IllegalArgumentException"}
而在控制檯,會輸出日誌:2019-01-04 14:39:21.437 ERROR com.qiqi.exception.GlobalExceptionHandler Line:118 - 非法輸入:【For input string: "3a"】
二:請求時發送POST請求(代碼中要求是GET請求)
前端響應:{"code":"405","msg":"Request method not Allowed"}
後臺日誌:2019-01-04 14:41:41.963 ERROR com.qiqi.exception.GlobalExceptionHandler Line:58 - http請求的方法不正確:【Request method 'POST' not supported】
三:在程序中拋出IOException
IOException異常沒有單獨捕獲,被捕獲在allExceptionHandler方法中,在這個方法中打印出了異常的詳細信息,包括出現異常的代碼行號等,同時還打印了異常的堆棧信息。
前端響應:{"code":"50","msg":"system Exception"}
後臺日誌:
2019-01-04 14:57:08.583 ERROR com.qiqi.exception.GlobalExceptionHandler Line:145 - 具體錯誤信息:【java.io.IOException
at com.qiqi.service.UserService.getUser(UserService.java:42)
at com.qiqi.controller.UserController.getUser(UserController.java:47)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:189)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:102)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:895)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:800)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1038)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:942)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1005)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:897)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:634)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:882)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:741)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:92)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:93)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:199)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:490)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:408)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:791)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1417)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:745)
】
2019-01-04 14:57:08.587 ERROR com.qiqi.exception.GlobalExceptionHandler Line:148 - com.qiqi.service.UserService.getUser(UserService.java:42)
2019-01-04 14:57:08.587 ERROR com.qiqi.exception.GlobalExceptionHandler Line:148 - com.qiqi.controller.UserController.getUser(UserController.java:47)
2019-01-04 14:57:08.587 ERROR com.qiqi.exception.GlobalExceptionHandler Line:148 - sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
2019-01-04 14:57:08.588 ERROR com.qiqi.exception.GlobalExceptionHandler Line:148 - sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
2019-01-04 14:57:08.588 ERROR com.qiqi.exception.GlobalExceptionHandler Line:148 - sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
2019-01-04 14:57:08.588 ERROR com.qiqi.exception.GlobalExceptionHandler Line:148 - java.lang.reflect.Method.invoke(Method.java:497)
2019-01-04 14:57:08.588 ERROR com.qiqi.exception.GlobalExceptionHandler Line:148 - org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:189)
2019-01-04 14:57:08.588 ERROR com.qiqi.exception.GlobalExceptionHandler Line:148 - org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138)
2019-01-04 14:57:08.588 ERROR com.qiqi.exception.GlobalExceptionHandler Line:148 - org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:102)
2019-01-04 14:57:08.588 ERROR com.qiqi.exception.GlobalExceptionHandler Line:148 - org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:895)
2019-01-04 14:57:08.589 ERROR com.qiqi.exception.GlobalExceptionHandler Line:148 - org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:800)
2019-01-04 14:57:08.589 ERROR com.qiqi.exception.GlobalExceptionHandler Line:148 - org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
2019-01-04 14:57:08.589 ERROR com.qiqi.exception.GlobalExceptionHandler Line:148 - org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1038)
2019-01-04 14:57:08.589 ERROR com.qiqi.exception.GlobalExceptionHandler Line:148 - org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:942)
2019-01-04 14:57:08.589 ERROR com.qiqi.exception.GlobalExceptionHandler Line:148 - org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1005)