關於Web應用的全局異常處理,上一篇介紹了ControllerAdvice
結合@ExceptionHandler
的方式來實現web應用的全局異常管理;css
本篇博文則帶來另一種並不常見的使用方式,經過實現自定義的HandlerExceptionResolver
,來處理異常狀態html
上篇博文連接: SpringBoot系列教程web篇之全局異常處理
本篇原文: SpringBoot系列教程web篇之自定義異常處理HandlerExceptionResolver
<!-- more -->java
首先得搭建一個web應用纔有可能繼續後續的測試,藉助SpringBoot搭建一個web應用屬於比較簡單的活;git
建立一個maven項目,pom文件以下github
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.7</version> <relativePath/> <!-- lookup parent from update --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <spring-cloud.version>Finchley.RELEASE</spring-cloud.version> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.45</version> </dependency> </dependencies> <build> <pluginManagement> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </pluginManagement> </build> <repositories> <repository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://repo.spring.io/milestone</url> <snapshots> <enabled>false</enabled> </snapshots> </repository> </repositories>
HandlerExceptionResolver
顧名思義,就是處理異常的類,接口就一個方法,出現異常以後的回調,四個參數中還攜帶了異常堆棧信息web
@Nullable ModelAndView resolveException( HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex);
咱們自定義異常處理類就比較簡單了,實現上面的接口,而後將完整的堆棧返回給調用方spring
public class SelfExceptionHandler implements HandlerExceptionResolver { @Override public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { String msg = GlobalExceptionHandler.getThrowableStackInfo(ex); try { response.addHeader("Content-Type", "text/html; charset=UTF-8"); response.getWriter().append("自定義異常處理!!! \n").append(msg).flush(); } catch (Exception e) { e.printStackTrace(); } return null; } } // 堆棧信息打印方法以下 public static String getThrowableStackInfo(Throwable e) { ByteArrayOutputStream buf = new ByteArrayOutputStream(); e.printStackTrace(new java.io.PrintWriter(buf, true)); String msg = buf.toString(); try { buf.close(); } catch (Exception t) { return e.getMessage(); } return msg; }
仔細觀察上面的代碼實現,有下面幾個點須要注意json
response.addHeader("Content-Type", "text/html; charset=UTF-8");
若是沒有這一行,會出現中文亂碼的狀況response.getWriter().append("自定義異常處理!!! \n").append(msg).flush();
; 若是項目中有自定義的錯誤頁面,能夠經過返回ModelAndView
來肯定最終返回的錯誤頁面WebMvcConfigurer
的子類中實現註冊,實例以下@SpringBootApplication public class Application implements WebMvcConfigurer { @Override public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) { resolvers.add(0, new SelfExceptionHandler()); } public static void main(String[] args) { SpringApplication.run(Application.class); } }
咱們依然使用上篇博文的用例來測試後端
@Controller @RequestMapping(path = "page") public class ErrorPageRest { @ResponseBody @GetMapping(path = "divide") public int divide(int sub) { return 1000 / sub; } }
下面分別是404異常和500異常的實測狀況websocket
500異常會進入咱們的自定義異常處理類, 而404依然走的是默認的錯誤頁面,因此若是咱們須要捕獲404異常,依然須要在配置文件中添加
# 出現錯誤時, 直接拋出異常 spring.mvc.throw-exception-if-no-handler-found=true # 設置靜態資源映射訪問路徑 spring.mvc.static-path-pattern=/statics/** # spring.resources.add-mappings=false
爲何404須要額外處理?
下面儘可能以通俗易懂的方式說明下這個問題
@ResponseBody
來代表一個url返回的是json數據(一般狀況下是這樣的,不考慮自定義實現)@Controller
中經過@RequestMapping
定義的REST服務,返回的是靜態資源NoHandlerFoundException
,不拋異常,而是到靜態資源中去找了(靜態資源中也沒有,爲啥不拋NoHandlerFoundException呢?這個異常表示這個url請求沒有對應的處理器,可是咱們這裏呢,給它分配到了靜態資源處理器了ResourceHttpRequestHandler
)針對上面這點,若是有興趣深挖的同窗,這裏給出關鍵代碼位置
// 進入方法: `org.springframework.web.servlet.DispatcherServlet#doDispatch` // debug 節點 Determine handler for the current request. mappedHandler = getHandler(processedRequest); if (mappedHandler == null) { noHandlerFound(processedRequest, response); return; } // 核心邏輯 // org.springframework.web.servlet.DispatcherServlet#getHandler @Nullable protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { if (this.handlerMappings != null) { for (HandlerMapping hm : this.handlerMappings) { if (logger.isTraceEnabled()) { logger.trace( "Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'"); } HandlerExecutionChain handler = hm.getHandler(request); if (handler != null) { return handler; } } } return null; }
本篇博文雖然也介紹了一種新的全局異常處理方式,實現效果和ControllerAdvice
也差很少,可是並不推薦用這種方法, 緣由以下
HandlerExceptionResolver
的方式沒有ControllerAdvice
方式簡介優雅DefaultHandlerExceptionResolver
已經很是強大了,基本上覆蓋了http的各類狀態碼,咱們本身再去定製的必要性不大盡信書則不如,以上內容,純屬一家之言,因我的能力有限,不免有疏漏和錯誤之處,如發現bug或者有更好的建議,歡迎批評指正,不吝感激
下面一灰灰的我的博客,記錄全部學習和工做中的博文,歡迎你們前去逛逛