精講RestTemplate第7篇-自定義請求失敗異常處理

字母哥部落格發表於2020-08-14

本文是精講RestTemplate第7篇,前篇的blog訪問地址如下:

一、異常現象

在使用RestTemplate進行遠端介面服務呼叫的時候,當請求的服務出現異常:超時、服務不存在等情況的時候(響應狀態非200、而是400、500HTTP狀態碼),就會丟擲如下異常:

該異常我是模擬出來的,將正確的請求服務地址由“/posts/1”改成“/postss/1”。服務不存在所以丟擲404異常。

@Test
public void testEntity() {
   String url = "http://jsonplaceholder.typicode.com/postss/1";
   ResponseEntity<String> responseEntity
               = restTemplate.getForEntity(url, String.class);  //這行丟擲異常
   //下面兩行程式碼執行不到
   HttpStatus statusCode = responseEntity.getStatusCode(); // 獲取響應碼
   System.out.println("HTTP 響應狀態:" + statusCode);
}

異常丟擲之後,程式後面的程式碼就執行不到了,無法進行後面的程式碼執行。實際的業務開發中,有的時候我們更期望的結果是:不管你服務端是超時了還是服務不存在,我們都應該獲得最終的請求結果(HTTP請求結果狀態400、500),而不是獲得一個丟擲的異常。

二、原始碼解析-預設實現

首先我要說一個結論:RestTemplate請求結果異常是可以自定義處理的。在開始進行自定義的異常處理邏輯之前,我們有必要看一下異常處理的預設實現。也就是:為什麼會產生上面小節提到的現象?

  • ResponseErrorHandler是RestTemplate請求結果的異常處理器介面
    • 介面的第一個方法hasError用於判斷HttpResponse是否是異常響應(通過狀態碼)
    • 介面的第二個方法handleError用於處理異常響應結果(非200狀態碼段)
  • DefaultResponseErrorHandler是ResponseErrorHandler的預設實現

所以我們就來看看DefaultResponseErrorHandler是如何來處理異常響應的?從HttpResponse解析出Http StatusCode,如果狀態碼StatusCode為null,就丟擲UnknownHttpStatusCodeException異常。

如果StatusCode存在,則解析出StatusCode的series,也就是狀態碼段(除了200段,其他全是異常狀態碼),解析規則是StatusCode/100取整。

public enum Series {

   INFORMATIONAL(1),  // 1xx/100
   SUCCESSFUL(2),  // 2xx/100
   REDIRECTION(3), // 3xx/100
   CLIENT_ERROR(4), // 4xx/100   ,客戶端異常
   SERVER_ERROR(5); // 5xx/100 ,服務端異常
}

進一步針對客戶端異常和服務端異常進行處理,處理的方法是丟擲HttpClientErrorException。也就是第一小節出現的異常的原因

三、RestTemplate自定義異常處理

所以我們要實現自定義異常,實現ResponseErrorHandler 介面就可以。

public class MyRestErrorHandler implements ResponseErrorHandler {

    /**
     * 判斷返回結果response是否是異常結果
     * 主要是去檢查response 的HTTP Status
     * 仿造DefaultResponseErrorHandler實現即可
     */
    @Override
    public boolean hasError(ClientHttpResponse response) throws IOException {
        int rawStatusCode = response.getRawStatusCode();
        HttpStatus statusCode = HttpStatus.resolve(rawStatusCode);
        return (statusCode != null ? statusCode.isError(): hasError(rawStatusCode));
    }

    protected boolean hasError(int unknownStatusCode) {
        HttpStatus.Series series = HttpStatus.Series.resolve(unknownStatusCode);
        return (series == HttpStatus.Series.CLIENT_ERROR || series == HttpStatus.Series.SERVER_ERROR);
    }
 
    @Override
    public void handleError(ClientHttpResponse response) throws IOException {
        // 裡面可以實現你自己遇到了Error進行合理的處理
        //TODO 將介面請求的異常資訊持久化
    }
}

將MyRestErrorHandler 在RestTemplate例項化的時候進行註冊。參考: 《精講RestTemplate第1篇-在Spring或非Spring環境下如何使用》《精講RestTemplate第2篇-多種底層HTTP客戶端類庫的切換》 進行實現

這時再去執行第一小節中的示例程式碼,就不會丟擲異常。而是得到一個HTTP Status 404的結果。我們可以根據這個結果,在程式中繼續向下執行程式碼。

歡迎關注我的部落格,裡面有很多精品合集

  • 本文轉載註明出處(必須帶連線,不能只轉文字):字母哥部落格

覺得對您有幫助的話,幫我點贊、分享!您的支援是我不竭的創作動力! 。另外,筆者最近一段時間輸出瞭如下的精品內容,期待您的關注。

相關文章