国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

SpringBoot RESTful 應用中的異常處理小結

jackzou / 1164人閱讀

摘要:和的區別方法注解作用于級別注解為一個定義一個異常處理器類注解作用于整個工程注解定義了一個全局的異常處理器需要注意的是的優先級比高即拋出的異常如果既可以讓標注的方法處理又可以讓標注的類中的方法處理則優先讓標注的方法處理處理中的異常為了方便地展

@ControllerAdvice 和 @ExceptionHandler 的區別

ExceptionHandler, 方法注解, 作用于 Controller 級別. ExceptionHandler 注解為一個 Controler 定義一個異常處理器.

ControllerAdvice, 類注解, 作用于 整個 Spring 工程. ControllerAdvice 注解定義了一個全局的異常處理器.

需要注意的是, ExceptionHandler 的優先級比 ControllerAdvice 高, 即 Controller 拋出的異常如果既可以讓 ExceptionHandler 標注的方法處理, 又可以讓 ControllerAdvice 標注的類中的方法處理, 則優先讓 ExceptionHandler 標注的方法處理.

處理 Controller 中的異常

為了方便地展示 Controller 異常處理的方式, 我創建了一個工程 SpringBootRESTfulErrorHandler, 其源碼可以到我的 Github: github.com/yongshun 中找到.
SpringBootRESTfulErrorHandler 工程的目錄結構如下:

首先我們定義了三個自定義的異常:
BaseException:

public class BaseException extends Exception {
    public BaseException(String message) {
        super(message);
    }
}

MyException1:

public class MyException1 extends BaseException {
    public MyException1(String message) {
        super(message);
    }
}

MyException2:

public class MyException2 extends BaseException {
    public MyException2(String message) {
        super(message);
    }
}

接著我們在 DemoController 中分別拋出這些異常:

@RestController
public class DemoController {
    private Logger logger = LoggerFactory.getLogger("GlobalExceptionHandler");

    @RequestMapping("/ex1")
    public Object throwBaseException() throws Exception {
        throw new BaseException("This is BaseException.");
    }

    @RequestMapping("/ex2")
    public Object throwMyException1() throws Exception {
        throw new MyException1("This is MyException1.");
    }

    @RequestMapping("/ex3")
    public Object throwMyException2() throws Exception {
        throw new MyException2("This is MyException1.");
    }

    @RequestMapping("/ex4")
    public Object throwIOException() throws Exception {
        throw new IOException("This is IOException.");
    }

    @RequestMapping("/ex5")
    public Object throwNullPointerException() throws Exception {
        throw new NullPointerException("This is NullPointerException.");
    }

    @ExceptionHandler(NullPointerException.class)
    public String controllerExceptionHandler(HttpServletRequest req, Exception e) {
        logger.error("---ControllerException Handler---Host {} invokes url {} ERROR: {}", req.getRemoteHost(), req.getRequestURL(), e.getMessage());
        return e.getMessage();
    }
}

/ex1: 拋出 BaseException

/ex2: 拋出 MyException1

/ex3: 拋出 MyException2

/ex4: 拋出 IOException

/ex5: 拋出 NullPointerException

當 DemoController 拋出未捕獲的異常時, 我們在 GlobalExceptionHandler 中進行捕獲并處理:
GlobalExceptionHandler:

@RestController
@ControllerAdvice
public class GlobalExceptionHandler {
    private Logger logger = LoggerFactory.getLogger("GlobalExceptionHandler");

    @ExceptionHandler(value = BaseException.class)
    @ResponseBody
    public Object baseErrorHandler(HttpServletRequest req, Exception e) throws Exception {
        logger.error("---BaseException Handler---Host {} invokes url {} ERROR: {}", req.getRemoteHost(), req.getRequestURL(), e.getMessage());
        return e.getMessage();
    }

    @ExceptionHandler(value = Exception.class)
    @ResponseBody
    public Object defaultErrorHandler(HttpServletRequest req, Exception e) throws Exception {
        logger.error("---DefaultException Handler---Host {} invokes url {} ERROR: {}", req.getRemoteHost(), req.getRequestURL(), e.getMessage());
        return e.getMessage();
    }
}

我們看到, GlobalExceptionHandler 類有兩個注解:

RestController, 表明 GlobalExceptionHandler 是一個 RESTful Controller, 即它會以 RESTful 的形式返回回復.

ControllerAdvice, 表示 GlobalExceptionHandler 是一個全局的異常處理器.

在 GlobalExceptionHandler 中, 我們使用了 ExceptionHandler 注解標注了兩個方法:

ExceptionHandler(value = BaseException.class): 表示 baseErrorHandler 處理 BaseException 異常和其子異常.

ExceptionHandler(value = Exception.class): 表示 defaultErrorHandler 會處理 Exception 異常和其所用子異常.

要注意的是, 和 try...catch 語句塊, 異常處理的順序也是從具體到一般, 即如果 baseErrorHandler 可以處理此異常, 則調用此方法來處理異常, 反之使用 defaultErrorHandler 來處理異常.

既然我們已經實現了 Controller 的異常處理, 那么接下來我們就來測試一下吧.
在瀏覽器中分別訪問這些鏈接, 結果如下:
/ex1:

/ex2:

/ex3:

/ex4:

/ex5:

可以看到, /ex1, /ex2, /ex3 拋出的異常都由 GlobalExceptionHandler.baseErrorHandler 處理; /ex4 拋出的 IOException 異常由 GlobalExceptionHandler.defaultErrorHandler 處理. 但是 /ex5 拋出的 NullPointerException 異常為什么不是 defaultErrorHandler 處理, 而是由 controllerExceptionHandler 來處理呢? 回想到 @ControllerAdvice 和 @ExceptionHandler 的區別 這以小節中的內容時, 我們就知道原因了: 因為我們在 DemoController 中使用 ExceptionHandler 注解定義了一個 Controller 級的異常處理器, 這個級別的異常處理器的優先級比全局的異常處理器優先級高, 因此 Spring 發現 controllerExceptionHandler 可以處理 NullPointerException 異常時, 就調用這個方法, 而不會調用全局的 defaultErrorHandler 方法了.

處理 404 錯誤 Spring MVC

SpringBoot 默認提供了一個全局的 handler 來處理所有的 HTTP 錯誤, 并把它映射為 /error. 當發生一個 HTTP 錯誤, 例如 404 錯誤時, SpringBoot 內部的機制會將頁面重定向到 /error 中.
例如下圖中是一個默認的 SpringBoot 404 異常頁面.

這個頁面實在是太丑了, 我們能不能自定義一個異常頁面呢? 當然可以了, 并且 SpringBoot 也給我們提示了: This application has no explicit mapping for /error, so you are seeing this as a fallback.
因此我們實現一個 /error 映射的 Controller 即可.

public class HttpErrorHandler implements ErrorController {

    private final static String ERROR_PATH = "/error";

    /**
     * Supports the HTML Error View
     *
     * @param request
     * @return
     */
    @RequestMapping(value = ERROR_PATH, produces = "text/html")
    public String errorHtml(HttpServletRequest request) {
        return "404";
    }

    /**
     * Supports other formats like JSON, XML
     *
     * @param request
     * @return
     */
    @RequestMapping(value = ERROR_PATH)
    @ResponseBody
    public Object error(HttpServletRequest request) {
        return "404";
    }

    /**
     * Returns the path of the error page.
     *
     * @return the error path
     */
    @Override
    public String getErrorPath() {
        return ERROR_PATH;
    }
}

根據上面代碼我們看到, 為了實現自定義的 404 頁面, 我們實現了 ErrorController 接口:

public interface ErrorController {
    String getErrorPath();
}

這個接口只有一個方法, 當出現 HTTP 錯誤時, SpringBoot 會將頁面重定向到 getErrorPath 方法返回的頁面中. 這樣我們就可以實現自定義的錯誤頁面了.

RESTful API

提供一個自定義的 "/error" 頁面對 Spring MVC 的服務來說自然是沒問題的, 但是如果我們的服務是一個 RESTful 服務的話, 這樣做就不行了.
當用戶調用了一個不存在的 RESTful API 時, 我們想記錄下這個異常訪問, 并返回一個代表錯誤的 JSON 給客戶端, 這該怎么實現呢?
我們很自然地想到, 我們可以使用處理異常的那一套來處理 404 錯誤碼.
那么我們來試一下這個想法是否可行吧.

奇怪的是, 當我們在瀏覽器中隨意輸入一個路徑時, 代碼并沒有執行到異常處理邏輯中, 而是返回了一個 HTML 頁面給我們, 這又是怎么回事呢?
原來 Spring Boot 中, 當用戶訪問了一個不存在的鏈接時, Spring 默認會將頁面重定向到 **/error** 上, 而不會拋出異常.
既然如此, 那我們就告訴 Spring Boot, 當出現 404 錯誤時, 拋出一個異常即可. 在 application.properties 中添加兩個配置:

spring.mvc.throw-exception-if-no-handler-found=true
spring.resources.add-mappings=false

上面的配置中, 第一個 spring.mvc.throw-exception-if-no-handler-found 告訴 SpringBoot 當出現 404 錯誤時, 直接拋出異常. 第二個 spring.resources.add-mappings 告訴 SpringBoot 不要為我們工程中的資源文件建立映射. 這兩個配置正是 RESTful 服務所需要的.
當加上這兩個配置后, 我們再來試一下:

可以看到, 現在確實是在 defaultErrorHandler 中處理了.

本文由 yongshun 發表于個人博客, 采用署名-非商業性使用-相同方式共享 3.0 中國大陸許可協議.
非商業轉載請注明作者及出處. 商業轉載請聯系作者本人
Email: yongshun1228@gmail.com
本文標題為: SpringBoot RESTful 應用中的異常處理小結
本文鏈接為: https://segmentfault.com/a/1190000006749441

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/65073.html

相關文章

  • Web開發框架推導

    摘要:邊界清晰,有利于理解開發測試和部署。前后端分離考慮到目前開發流行前后端分離,為了適應潮流,引入前后端分離的約束。該請求被接受處理,但是該處理是不完整的。 本文欲回答這樣一個問題:在 「特定環境 」下,如何規劃Web開發框架,使其能滿足 「期望 」? 假設我們的「特定環境 」如下: 技術層面 使用Java語言進行開發 通過Maven構建 基于SpringBoot 使用Intelli...

    vpants 評論0 收藏0
  • SpringBoot就是這么簡單

    摘要:熱加載代表的是我們不需要重啟服務器,就能夠類檢測得到,重新生成類的字節碼文件無論是熱部署或者是熱加載都是基于類加載器來完成的。驗證階段字節碼文件不會對造成危害準備階段是會賦初始值,并不是程序中的值。 一、SpringBoot入門 今天在慕課網中看見了Spring Boot這么一個教程,這個Spring Boot作為JavaWeb的學習者肯定至少會聽過,但我是不知道他是什么玩意。 只是大...

    whinc 評論0 收藏0
  • Spring Boot 2.x 系列教程:WebFlux REST API 全局異常處理 Error

    摘要:挺多人咨詢的,異常處理用切面注解去實現去全局異常處理。全局異常處理類,代碼如下代碼解析如下抽象類是用來處理全局錯誤時進行擴展和實現注解標記的切面排序,值越小擁有越高的優先級,這里設置優先級偏高。 本文內容 為什么要全局異常處理? WebFlux REST 全局異常處理實戰 小結 摘錄:只有不斷培養好習慣,同時不斷打破壞習慣,我們的行為舉止才能夠自始至終都是正確的。 一、為什么要全局...

    BicycleWarrior 評論0 收藏0
  • SpringBoot基礎篇AOP之基本使用姿勢小結

    摘要:通知和切點共同定義了關于切面的全部內容,它是什么時候,在何時和何處完成功能引入允許我們向現有的類添加新的方法或者屬性組裝方面來創建一個被通知對象。這可以在編譯時完成例如使用編譯器,也可以在運行時完成。和其他純框架一樣,在運行時完成織入。 原文:190301-SpringBoot基礎篇AOP之基本使用姿勢小結 一般來講,談到Spring的特性,繞不過去的就是DI(依賴注入)和AOP(切...

    timger 評論0 收藏0
  • Java經典

    摘要:請注意,我們在聊聊單元測試遇到問題多思考多查閱多驗證,方能有所得,再勤快點樂于分享,才能寫出好文章。單元測試是指對軟件中的最小可測試單元進行檢查和驗證。 JAVA容器-自問自答學HashMap 這次我和大家一起學習HashMap,HashMap我們在工作中經常會使用,而且面試中也很頻繁會問到,因為它里面蘊含著很多知識點,可以很好的考察個人基礎。但一個這么重要的東西,我為什么沒有在一開始...

    xcold 評論0 收藏0

發表評論

0條評論

jackzou

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<