Spring Security技術棧學習筆記(四)RESTful API服務異常處理

當我們從瀏覽器訪問不存在的Spring BootRESTful API的時候,往往會返回Spring Boot內置的404錯誤界面,但是作爲前後端分離的應用,相同的API也許會在其他終端訪問,比如手機APP等,那麼也會是相同的處理方式嗎?

一、Spring Boot的默認處理方式分析
  • 從瀏覽器端訪問

啓動Spring Boot項目,從瀏覽器訪問一個不存在的API,如“/user/hello”,這時候返回來的是一個HTML頁面,如下圖所示:
Spring Boot默認的404界面

  • APP端訪問

APP端訪問我們可以使用模擬RESTful API發送器來進行發送,我這裏使用的Paw軟件,你可以在你的谷歌瀏覽器上安裝Postman來進行發送。訪問“/user/hello”返回的結果如下如所示:
從客戶端訪問返回的結果

對比上面的兩種訪問方式,返回的錯誤類型是不一樣的,瀏覽器訪問返回的是一個HTML頁面,而客戶端訪問返回的是一個JSON數據。那麼問題來了,Spring Boot是如何確定當前請求來自瀏覽器還是客戶端?我們可以從它的源代碼中找到答案。
這段源代碼來自於Spring Boot的一個包org.springframework.boot.autoconfigure.web中的BasicErrorController,從類名就可以知道它是一個Controller,且處理的路徑就是“/error”。找到錯誤處理的兩個方法,如下圖所示:
這裏寫圖片描述

第一個方法和第二個方法處理的都是同一個API,區別就在於第一個方法的@RequestMapping裏面包含一個produces屬性,它表示將生成什麼類型的資源返回給前端,很明顯,第一個方法要返回的是一個HTML頁面,而第二個方法返回的是JSON數據。這就很明瞭了,當瀏覽器訪問錯誤的API的時候,會自動進入第一個方法處理錯誤,從客戶端訪問的時候,就會進入第二個方法處理錯誤。當然,從瀏覽器發送的請求的時候,我們可以看見請求頭中看到瀏覽器要求的返回數據類型就包含了text/html,如下圖所示:
這裏寫圖片描述

以上的例子都是訪問資源不存在的案例,訪問的處理邏輯並未進入對應的Controller就被Spring Boot打回去了,如果是服務代碼拋出了異常,Spring Boot是如何處理的呢?在這裏我再寫一個Controller,手動拋出異常。代碼如下:

@GetMapping("/user5")
@ResponseBody
public User user5() {
    throw new RuntimeException("User is not exist.");
}

從瀏覽器端訪問http://localhost:8080/user5,返回的依然是HTML頁面,如下所示:
這裏寫圖片描述
而從客戶端訪問,返回的依然是JSON數據,如下圖所示:
這裏寫圖片描述

二、自定義服務異常處理

在實際的開發過程中,如果出現404或者500的錯誤的時候,返回給瀏覽器是Spring Boot默認的處理界面,這並不友好,我們可以實現自定義頁面來給出更好的溫馨提示。

  • 瀏覽器端自定義處理方式

這裏僅僅介紹一種最簡單的方式來處理異常,在resources文件夾下再建立一個resources文件夾,然後再在新建的resources文件夾下建立一個error,在error文件夾裏面建立404.html500.html,在訪問出現404錯誤的時候,就會跳轉到我們自己定義的HTML中,而不是Spring Boot默認的界面。

  • 自定義服務異常處理類
    在實際的開發中,我們完全可以自定義服務異常處理類,以滿足實際的開發需求。這裏寫一個異常類,在業務邏輯處理中,可以根據需要手動拋出自己自定義的異常。比如:
package com.lemon.security.web.exception;

import lombok.Data;
import lombok.EqualsAndHashCode;

/**
 * @author lemon
 * @date 2018/4/1 下午2:08
 */
@EqualsAndHashCode(callSuper = true)
@Data
public class UserNotExistException extends RuntimeException {

    private Integer id;

    public UserNotExistException(Integer id) {
        super("user not exist.");
        this.id = id;
    }
}

爲了測試,在Controller添加一個方法,代碼如下:

@GetMapping("/user6/{id:\\d+}")
@ResponseBody
public User user6(@PathVariable Integer id) {
    throw new UserNotExistException(id);
}

當訪問這個API的時候,就會拋出我們自定義的異常,這時候,Spring Boot默認的處理方式返回的結果如下圖:
這裏寫圖片描述

有時候我們前端不需要這麼多的信息,只需要部分信息,這個時候就需要自定義異常處理了,而不是採用Spring Boot的默認處理方式了,在這裏,我們可以寫一個異常處理類,專門用來處理自定義異常。

package com.lemon.security.web.exception.handler;

import com.lemon.security.web.exception.UserNotExistException;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;

import java.util.HashMap;
import java.util.Map;

/**
 * 自定義錯誤處理邏輯
 *
 * @author lemon
 * @date 2018/4/1 下午2:48
 */
@ControllerAdvice
public class UserNotExistExceptionHandler {

    @ExceptionHandler(UserNotExistException.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ResponseBody
    public Map<String, Object> handleUserNotExistException(UserNotExistException ex) {
        Map<String, Object> result = new HashMap<>();
        result.put("id", ex.getId());
        result.put("message", ex.getMessage());
        return result;
    }
}

當一個類加上了@ControllerAdvice註解,那麼這個類就具備了處理其他Controller異常的能力,具體的處理方式還是通過方法來進行的。上面的方法就是專門來處理UserNotExistException這個異常的,@ExceptionHandler就是指定了需要被處理的異常,@ResponseStatus指定狀態碼,最後將處理後的數據返回。定義好這個類之後,當代碼中拋出了UserNotExistException異常的時候,都會轉到這個方法中進行處理。再次運行應用,訪問http://localhost:8080/user6/1返回的數據如下如所示:
這裏寫圖片描述
這就是我們自定義的異常處理後的數據了。

Spring Security技術棧開發企業級認證與授權系列文章列表:

Spring Security技術棧學習筆記(一)環境搭建
Spring Security技術棧學習筆記(二)RESTful API詳解
Spring Security技術棧學習筆記(三)表單校驗以及自定義校驗註解開發
Spring Security技術棧學習筆記(四)RESTful API服務異常處理
Spring Security技術棧學習筆記(五)使用Filter、Interceptor和AOP攔截REST服務
Spring Security技術棧學習筆記(六)使用REST方式處理文件服務
Spring Security技術棧學習筆記(七)使用Swagger自動生成API文檔
Spring Security技術棧學習筆記(八)Spring Security的基本運行原理與個性化登錄實現
Spring Security技術棧學習筆記(九)開發圖形驗證碼接口
Spring Security技術棧學習筆記(十)開發記住我功能
Spring Security技術棧學習筆記(十一)開發短信驗證碼登錄
Spring Security技術棧學習筆記(十二)將短信驗證碼驗證方式集成到Spring Security
Spring Security技術棧學習筆記(十三)Spring Social集成第三方登錄驗證開發流程介紹
Spring Security技術棧學習筆記(十四)使用Spring Social集成QQ登錄驗證方式
Spring Security技術棧學習筆記(十五)解決Spring Social集成QQ登錄後的註冊問題
Spring Security技術棧學習筆記(十六)使用Spring Social集成微信登錄驗證方式

示例代碼下載地址:

項目已經上傳到碼雲,歡迎下載,內容所在文件夾爲chapter004

更多幹貨分享,歡迎關注我的微信公衆號:爪哇論劍(微信號:itlemon)
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章