RestClient 和 WebClient 在異常處理上的具體差異
RestClient 和 WebClient 在異常處理上有顯著的不同,這些差異主要體現在異常處理機制、錯誤處理的復雜性和靈活性上。以下是兩者的詳細對比:【起飛嘎嘎飛LSIXSO】
一、RestClient 的異常處理
(一)異常處理機制
RestClient 提供了改進的錯誤處理機制,使得異常處理和 HTTP 狀態碼的管理變得更加簡單和直接。RestClient 在接收到客戶端錯誤狀態碼(400-499)或服務器錯誤狀態碼(500-599)時,會拋出 RestClientException 的子類。
(二)自定義錯誤處理器
RestClient 支持通過 defaultStatusHandler 方法定義全局的錯誤處理器,也可以通過 onStatus 方法為特定請求定義錯誤處理器。例如:
java復制
RestClient restClient = RestClient.builder()
.baseUrl(properties.getUrl())
.defaultHeader(HttpHeaders.AUTHORIZATION, encodeBasic("pig", "pig"))
.defaultStatusHandler(
HttpStatusCode::is4xxClientError,
(request, response) -> {
logger.error("Client Error Status " + response.getStatusCode());
logger.error("Client Error Body " + new String(response.getBody().readAllBytes()));
}
)
.build();
在運行刪除命令行運行程序后,控制臺的輸出如下:
復制
Client Error Status 404 NOT_FOUND
Client Error Body {"status":404,"message":"Entity Customer for id 2 was not found.","timestamp":"2023-07-23T09:24:55.4088208"}
另一種選擇是為刪除操作實現 onStatus 方法。它優先于 RestClient 默認處理程序行為:
java復制
ResponseEntity response = restClient.delete()
.uri("/{id}", 2)
.accept(MediaType.APPLICATION_JSON)
.retrieve()
.onStatus(HttpStatusCode::is4xxClientError,
(req, res) -> logger.error("Couldn't delete " + res.getStatusText())
)
.toBodilessEntity();
現在控制臺中的消息將是:Couldn't delete Not Found
(三)優點
- 簡化錯誤處理:RestClient 提供了改進的錯誤處理機制,使得異常處理和 HTTP 狀態碼的管理變得更加簡單和直接。
- 靈活的錯誤處理器:支持全局和局部的錯誤處理器,可以根據需要靈活配置。
(四)缺點
- 功能有限:雖然 RestClient 提供了改進的錯誤處理機制,但其功能相對 WebClient 較為有限。
二、WebClient 的異常處理
(一)異常處理機制
WebClient 提供了強大的錯誤處理功能,支持響應式編程模型。WebClient 的錯誤處理通常使用 onErrorResume、onErrorReturn 等操作符。例如:
java復制
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;
public static Mono<String> makePostRequestAsync(String url, String postData) {
WebClient webClient = WebClient.builder()
.baseUrl(url)
.build();
return webClient.post()
.uri("/")
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.body(BodyInserters.fromFormData("data", postData))
.retrieve()
.onStatus(HttpStatus::is4xxClientError, clientResponse -> Mono.error(new RuntimeException("Client error")))
.onStatus(HttpStatus::is5xxServerError, clientResponse -> Mono.error(new RuntimeException("Server error")))
.bodyToMono(String.class);
}
在此示例中,onStatus() 方法被調用兩次,一次針對 4xx 客戶端錯誤,一次針對 5xx 服務器錯誤。onStatus() 每次調用都采用兩個參數:
- Predicate:確定錯誤狀態代碼是否與條件匹配。
- Function:用于返回 Mono,即要傳播到訂閱者的錯誤信息。
- 如果狀態代碼與條件匹配,Mono 則會發出相應的狀態代碼,并且 Mono 鏈會因錯誤而終止。在此示例中,Mono 將發出一條 RuntimeException 錯誤消息,指示該錯誤是客戶端錯誤還是服務器錯誤。
(二)錯誤處理的復雜性
WebClient 的錯誤處理可能更復雜,需要開發者在處理響應狀態、異常和重試機制時更加小心和全面。例如,以下代碼展示了如何處理成功響應和錯誤:
java復制
responseMono.subscribe(
response -> {
// handle the response
LOG.info("SUCCESS API Response {}", response);
},
error -> {
// handle the error
LOG.error("An error occurred: {}", error.getMessage());
LOG.error("error class: {}", error.getClass());
// Errors / Exceptions from Server
if (error instanceof WebClientResponseException) {
WebClientResponseException webClientResponseException =
(WebClientResponseException) error;
int statusCode = webClientResponseException.getStatusCode().value();
String statusText = webClientResponseException.getStatusText();
LOG.info("Error status code: {}", statusCode);
LOG.info("Error status text: {}", statusText);
if (statusCode >= 400 && statusCode < 500) {
LOG.info(
"Error Response body {}", webClientResponseException.getResponseBodyAsString());
}
Throwable cause = webClientResponseException.getCause();
LOG.error("webClientResponseException");
if (null != cause) {
LOG.info("Cause {}", cause.getClass());
if (cause instanceof ReadTimeoutException) {
LOG.error("ReadTimeout Exception");
}
if (cause instanceof TimeoutException) {
LOG.error("Timeout Exception");
}
}
}
// Client errors i.e. Timeouts etc -
if (error instanceof WebClientRequestException) {
LOG.error("webClientRequestException");
WebClientRequestException webClientRequestException =
(WebClientRequestException) error;
Throwable cause = webClientRequestException.getCause();
if (null != cause) {
LOG.info("Cause {}", cause.getClass());
if (cause instanceof ReadTimeoutException) {
LOG.error("ReadTimeout Exception");
}
if (cause instanceof ConnectTimeoutException) {
LOG.error("Connect Timeout Exception");
}
}
}
}
);
(三)優點
- 強大的錯誤處理:WebClient 提供了強大的錯誤處理功能,支持響應式編程模型。
- 靈活的錯誤處理:支持 onErrorResume、onErrorReturn 等操作符,可以靈活處理不同類型的錯誤。
(四)缺點
- 復雜性較高:WebClient 的錯誤處理可能更復雜,需要開發者在處理響應狀態、異常和重試機制時更加小心和全面。
三、總結
RestClient 和 WebClient 在異常處理上有顯著的不同:
- RestClient:提供了改進的錯誤處理機制,使得異常處理和 HTTP 狀態碼的管理更加簡單和直接。它支持全局和局部的錯誤處理器,可以根據需要靈活配置。
- WebClient:提供了強大的錯誤處理功能,支持響應式編程模型。它支持 onErrorResume、onErrorReturn 等操作符,可以靈活處理不同類型的錯誤。但其錯誤處理可能更復雜,需要開發者在處理響應狀態、異常和重試機制時更加小心和全面。
- 如果你的應用場景需要更簡單的錯誤處理機制,RestClient 是一個不錯的選擇。如果你需要更強大的錯誤處理功能,特別是響應式編程支持,WebClient 更適合。