知識庫 / HTTP Client-Side RSS 訂閱

Spring RestTemplate 錯誤處理

HTTP Client-Side,Spring Web
HongKong
9
02:01 PM · Dec 06 ,2025

1. 概述

在本簡短教程中,我們將討論如何在 ResponseErrorHandler 接口上實現並注入它,以便在將 HTTP 錯誤返回的遠程 API 中,以優雅的方式處理這些錯誤,在 RestTemplate 實例中。

2. 默認錯誤處理

默認情況下,<em >RestTemplate</em> 在發生 HTTP 錯誤時會拋出以下異常之一:

  1. `HttpClientErrorException` – 在 HTTP 狀態碼為 4xx 的情況下
  2. `HttpServerErrorException` – 在 HTTP 狀態碼為 5xx 的情況下
  3. `UnknownHttpStatusCodeException` – 在 HTTP 狀態碼未知的情況下

所有這些異常都是 <em >RestClientResponseException</em> 的擴展。

顯然,添加自定義錯誤處理的最簡單策略是將調用包裝在 <em >try/catch</em> 塊中。然後我們可以根據需要處理捕獲的異常。

然而,這種簡單策略隨着遠程 API 或調用的數量增加時,無法擴展。如果我們能夠為所有遠程調用實現一個可重用的錯誤處理程序,那將更有效。

3. 實現 ResponseErrorHandler

一個實現 ResponseErrorHandler 的類將讀取響應的 HTTP 狀態碼,並根據以下情況之一進行操作:

  1. 拋出一個對我們的應用程序有意義的異常
  2. 簡單地忽略 HTTP 狀態碼,讓響應流在不中斷的情況下繼續

我們需要將我們的 ResponseErrorHandler 實現注入到 RestTemplate 實例中。為此,我們使用 RestTemplateBuilder 來替換響應流中的默認錯誤處理程序。

3.1. 處理常見HTTP錯誤

首先,我們實現一個簡單的 <em >RestTemplateResponseErrorHandler</em>,用於處理常見的HTTP錯誤,例如 <em >4xx</em><em >5xx</em> 狀態碼。例如,我們通過拋出自定義的 <em >NotFoundException</em> 來處理 <em >404</em> 錯誤:

@Component
public class RestTemplateResponseErrorHandler implements ResponseErrorHandler {

    @Override
    public boolean hasError(ClientHttpResponse httpResponse) throws IOException {
        return httpResponse.getStatusCode().is5xxServerError() || 
            httpResponse.getStatusCode().is4xxClientError();
    }

    @Override
    public void handleError(ClientHttpResponse httpResponse) throws IOException {
        if (httpResponse.getStatusCode().is5xxServerError()) {
            //Handle SERVER_ERROR
            throw new HttpClientErrorException(httpResponse.getStatusCode());
        } else if (httpResponse.getStatusCode().is4xxClientError()) {
            //Handle CLIENT_ERROR
            if (httpResponse.getStatusCode() == HttpStatus.NOT_FOUND) {
                throw new NotFoundException();
            }
        }
    }
}

然後我們可以使用 RestTemplateBuilder 構建 RestTemplate 實例,並引入我們的 RestTemplateResponseErrorHandler

@Service
public class BarConsumerService {

    private RestTemplate restTemplate;

    @Autowired
    public BarConsumerService(RestTemplateBuilder restTemplateBuilder) {
        RestTemplate restTemplate = restTemplateBuilder
          .errorHandler(new RestTemplateResponseErrorHandler())
          .build();
    }

    public Bar fetchBarById(String barId) {
        return restTemplate.getForObject("/bars/4242", Bar.class);
    }

}

3.2. 處理 401 未授權並解析響應體

有時,當 API 返回 401 未授權 狀態時,響應體可能包含有用的信息,例如我們可能想要提取和使用的錯誤消息。

由於 ResponseErrorHandler.handleError() 方法不自動反序列化響應體,一種常見的方法是 捕獲 HttpStatusCodeException,該異常由 RestTemplate 拋出,並手動提取體 。為了更有效地處理 401 未授權狀態,我們可以將請求包裝在 try-catch 塊中,並在異常被拋出時檢查響應體:

public Bar fetchBarById(String barId) {
    try {
        return restTemplate.getForObject("/bars/" + barId, Bar.class);
    } catch (HttpStatusCodeException e) {
        if (e.getStatusCode() == HttpStatus.UNAUTHORIZED) {
            String responseBody = e.getResponseBodyAsString();
            throw new UnauthorizedException("Unauthorized access: " + responseBody);
        }
        throw e;
    }
}

這種方法允許我們以一種受控和信息化的方式處理 401 未授權 錯誤。通過捕獲異常並手動提取響應體,我們可以檢查服務器返回的任何錯誤詳情,例如指示身份驗證失敗的消息或解決問題的説明。這使我們能夠向用户提供更清晰的反饋,為故障排除記錄更詳細的消息,甚至根據響應內容觸發備用機制。

4. 測試我們的實現

最後,我們將通過模擬服務器並返回 NOT_FOUND 狀態碼來測試此處理程序:

@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = { NotFoundException.class, Bar.class })
@RestClientTest
public class RestTemplateResponseErrorHandlerIntegrationTest {

    @Autowired 
    private MockRestServiceServer server;
 
    @Autowired 
    private RestTemplateBuilder builder;

    @Test
    public void  givenRemoteApiCall_when404Error_thenThrowNotFound() {
        Assertions.assertNotNull(this.builder);
        Assertions.assertNotNull(this.server);

        RestTemplate restTemplate = this.builder
          .errorHandler(new RestTemplateResponseErrorHandler())
          .build();

        this.server
          .expect(ExpectedCount.once(), requestTo("/bars/4242"))
          .andExpect(method(HttpMethod.GET))
          .andRespond(withStatus(HttpStatus.NOT_FOUND));

        Assertions.assertThrows(NotFoundException.class, () -> {
            Bar response = restTemplate.getForObject("/bars/4242", Bar.class);
        });
    }
}

5. 結論

在本文中,我們展示了一種實現和測試針對 RestTemplate 的自定義錯誤處理器的解決方案,該方案可以將 HTTP 錯誤轉換為有意義的異常。

user avatar
0 位用戶收藏了這個故事!
收藏

發佈 評論

Some HTML is okay.