SpringBoot自適應異常處理

圖片

圖片

效果演示

咱們先來看一下Springboot的默認效果html

瀏覽器訪問

圖片

客戶端訪問

圖片

劃重點!!!

可是絕大部分公司的代碼,都是沒作自適應處理的,很大一部分緣由在於,你在網上搜索Springboot全局異常處理,都是搜索到這麼一段代碼!面試

@ControllerAdvice
public class MyControllerAdvice {
 
 
    @ResponseBody
    @ExceptionHandler(value = Exception.class)
    public ResponseEntity<?> errorHandler(Exception ex) {
       // 處理異常
    }

 
}

強烈建議先用本身經常使用的搜索引擎搜索一遍,而後再看一下本身公司代碼,看看是否是相似這麼一段代碼再往下看。編程

固然不少同窗可能會說,咱們就已經和客戶端約定很好了,只會有json,不會有返回html的場景。因此,不作這個適應,其實也是沒問題的。可是若是你是作基礎架構的同窗,這個功能你是必需要作的,由於你對接的是整個公司的業務部門,Springboot能作,你作相似的基礎組件,若是功能比Springboot還差,你讓業務方的同窗怎麼想?json

固然,對於絕大部分同窗來講,不作問題也不大。瀏覽器

可是這樣你會錯過一個很好的學習機會。什麼學習機會?由於不少同窗平時總說,面試造火箭,工做中遇到不懂的問題百度或者谷歌一下就行了。然而,這個問題,你就沒這麼好搜索到。也就是說,絕大部分人都是面向搜索引擎編程,當遇到搜索引擎沒法解決的問題的時候,就是體現你價值的時候,好好珍惜。微信

作不作這個功能我以爲不重要,這個寶貴的鍛鍊解決問題能力的機會是真的很可貴,畢竟,確實大部分功能是真的簡單搜索,或者肥朝交流羣問問就能解決。架構

自適應原理

不少同窗說,既然搜索不到,那果斷一波源碼走起。可是,Springboot源碼這麼多,請問哪裏入手?這個纔是重點!這個時候,咱們能夠官方文檔走一波。app

27.1.9 Error Handling框架

Spring Boot provides an /error mapping by default that handles all errors in a sensible way, and it is registered as a ‘global’ error page in the servlet container. For machine clients it will produce a JSON response with details of the error, the HTTP status and the exception message. For browser clients there is a ‘whitelabel’ error view that renders the same data in HTML format (to customize it just add a View that resolves to ‘error’). To replace the default behaviour completely you can implement ErrorController and register a bean definition of that type, or simply add a bean of type ErrorAttributes to use the existing mechanism but replace the contents.ide

一些同窗說,英文看不懂?我挑5個重點單詞給你,都是小學單詞,只要小學能畢業,我認爲都能看懂。

clients JSON browser HTML ErrorController

肥朝小聲逼逼:這裏特別強調,並非說看官方文檔是最優解決問題方案。仍是那句話,老司機都是看菜吃飯的,解決問題的套路有不少,時間有限,我就不把全部套路一個一個列出來(實際上是怕套路所有告訴大家了,大家就取關了!),直入主題就行。

從文檔和小學的英文單詞咱們把目標鎖定在了ErrorController,給你們看一下關鍵代碼

@Controller
@RequestMapping("${server.error.path:${error.path:/error}}")
public class BasicErrorController extends AbstractErrorController {

 @RequestMapping(produces = "text/html")
 public ModelAndView errorHtml(HttpServletRequest request,
   HttpServletResponse response)
 
{
  HttpStatus status = getStatus(request);
  Map<String, Object> model = Collections.unmodifiableMap(getErrorAttributes(
    request, isIncludeStackTrace(request, MediaType.TEXT_HTML)));
  response.setStatus(status.value());
  ModelAndView modelAndView = resolveErrorView(request, response, status, model);
  return (modelAndView != null) ? modelAndView : new ModelAndView("error", model);
 }

 @RequestMapping
 @ResponseBody
 public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
  Map<String, Object> body = getErrorAttributes(request,
    isIncludeStackTrace(request, MediaType.ALL));
  HttpStatus status = getStatus(request);
  return new ResponseEntity<Map<String, Object>>(body, status);
 }

}

從這裏咱們大體能夠猜想出,要作一個自適應的全局異常處理,理論上是要這麼寫的。

@ControllerAdvice
public class MyExceptionHandler  {

    @ExceptionHandler(Exception.class)
    public String handleExceptionHtml(Exception e, HttpServletRequest httpServletRequest) {
        // 這裏作一些你本身的處理,好比
        httpServletRequest.setAttribute("歡迎關注微信公衆號","肥朝");
        return "forward:/error";
    }

}

果真調試一波,發現果然如此。固然具體怎麼自定義這個錯誤界面之類的,網上一搜就有,因此這些不是肥朝的重點。那麼這個自適應全局異常彷佛美滋滋了?

遇到問題

咱們知道了這個自適應的全局異常處理的原理,也很容易想到怎麼弄出bug。好比,你在攔截器出現了異常的話。

@Configuration
public class MyMvcConfig extends WebMvcConfigurerAdapter {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new HandlerInterceptor() {
            @Override
            public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
                throw new RuntimeException("這裏僞裝拋出一個肥朝異常");
                //return true;
            }

            @Override
            public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {

            }

            @Override
            public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {

            }
        });
    }

}

那麼就會出現,StackOverflowError

由於攔截器出現異常,會掉進你的全局異常處理,而後你的全局異常處理,又進行forward,又進入了攔截器,而後一直循環。

那麼怎麼解決這個問題呢?咱們見招拆招,這個時候,我要演示常見的幾種不優雅,可是平時你們都容易作的寫法。

將配置寫死

registry.addInterceptor(new HandlerInterceptor() {
    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
        throw new RuntimeException("這裏僞裝拋出一個肥朝異常");
        //return true;
    }

    @Override
    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {

    }

    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {

    }
}).excludePathPatterns("/error");

咱們從

@RequestMapping("${server.error.path:${error.path:/error}}")

這裏得知,這個error.path是能夠配置的,不少同窗圖快,excludePathPatterns處寫死了/error,這樣一直用默認的天然沒問題,一旦人家配置了error.path,就出問題了。

潛規則

這個潛規則的問題,是絕大部分同窗寫代碼中最多見的問題。你想一下,你的自適應全局異常是解決了,可是,帶來的影響倒是,每個攔截器都要加上excludePathPatterns這麼一個配置。對於使用者來講,這個必須加上某個配置,就是一種潛規則,並且,對於新來的同事而言,他根本不知道這種潛規則,一旦潛規則的代碼多了,後續很難維護。

拓展思考

那麼不潛規則的代碼應該是怎麼樣的?

固然不少時候,咱們必需要潛規則!好比,大數據的同窗要求,送過來的日誌必定要有應用名。那麼,對於業務方而言,他就必需要配置應用名。那麼,如何讓業務方的同事知道這個潛規則。固然不少同窗說,那就直接告訴同事要加某些參數啊。你連肥朝天天的推文都不記得看,你能保證每一個同事都記得?

因此總結下來,咱們遇到這麼一類問題以下:

1.咱們須要對攔截器進行一些潛規則參數,好比本文這種,如何優雅潛規則?

2.好比攔截器有順序要求,好比咱們基礎框架定義了一個traceInterceptor的攔截器,這個攔截器就必須放在最前。那麼問題來了,你怎麼保證這個是最前的。有同窗就說了,那我用@Order控制啊。那我也寫一個和你同樣的攔截器,叫feichaoInterceptor,代碼和你如出一轍,既然如出一轍,你怎麼保證你的就比個人前了?

3.對於必需要潛規則的場景,如何在反抗的狀況下,也能潛到?

對於這幾個問題,咱們下期再解答。



圖片

相關文章
相關標籤/搜索