如何使用 Spring Boot 对 WebFlux 进行异常处理?
How to do Exception Handling for WebFlux using Springboot?
我有3个微服务应用。我正在尝试使用反应包中的 webclient 进行 2 get 异步调用,然后在收到响应时将它们组合起来。
示例代码:
(引用自 - https://docs.spring.io/spring/docs/5.1.9.RELEASE/spring-framework-reference/web-reactive.html#webflux-client-synchronous)
Mono<Person> personMono = client.get().uri("/person/{id}", personId)
.retrieve().bodyToMono(Person.class);
Mono<List<Hobby>> hobbiesMono = client.get().uri("/person/{id}/hobbies", personId)
.retrieve().bodyToFlux(Hobby.class).collectList();
Map<String, Object> data = Mono.zip(personMono, hobbiesMono, (person, hobbies) -> {
Map<String, String> map = new LinkedHashMap<>();
map.put("person", personName);
map.put("hobbies", hobbies);
return map;
})
.block();
我的问题是如何向 get 调用添加异常处理?
如何检查我收到的是 404、204 还是其他?
我试过:
- 将 .onStatus() 添加到 GET 调用
.onStatus(HttpStatus::is4xxClientError, clientResponse ->
Mono.error(new Data4xxException(String.format(
"Could not GET data with id: %s from another app, due to error:
%s", key, clientResponse))))
.onStatus(HttpStatus::is5xxServerError, clientResponse ->
Mono.error(new Data5xxException(
String.format("For Data %s, Error Occurred: %s", key, clientResponse))))
- 添加异常处理程序 - 但我确实没有控制器,所以这似乎不起作用。
@ExceptionHandler(WebClientException.class)
public Exception handlerWebClientException(WebClientException webClientException) {
return new Data4xxException("Testing", webClientException);
}
- 添加了一个 class,其中包含 ControllerAdvice 和 ExceptionHandler
@ControllerAdvice
public class WebFluxExceptionHandler {
@ExceptionHandler(WebClientException.class)
public Exception handlerWebClientException(WebClientException webClientException) {
return new Data4xxException("Testing", webClientException);
}
}
但我没有在 spring-boot 日志中看到它们。
Mono.zip.block() 方法只是 return 为 null,实际上并没有抛出任何异常。
如何让 zip 方法抛出异常而不是 return null?
你问的不是很清楚
“如何让 zip 方法抛出异常而不是 return null?”
在 WebFlux 中,您通常不会抛出异常,而是传播异常然后处理它们。为什么?因为我们正在处理数据流,如果你抛出异常,流结束,客户端断开连接,事件链停止。
我们仍然希望维护数据流并处理流经的不良数据。
您可以使用 doOnError
方法处理错误。
.onStatus(HttpStatus::is4xxClientError, clientResponse ->
Mono.error(new Data4xxException(String.format(
"Could not GET data with id: %s from another app, due to error:
%s", key, clientResponse))))
Mono.zip( .. ).doOnError( //Handle your error, log or whatever )
如果你想做一些更具体的事情,你必须用你希望如何处理你的错误来更新你的问题。
方法是按以下方式使用 onErrorMap:
Mono<Person> personMono = client.get()
.uri("/person/{id}", personId)
.retrieve()
.bodyToMono(Person.class)
.onErrorMap((Throwable error) -> error);
onErrorMap
将使 Mono 在 Zip 阻塞时实际抛出错误,终止 zip 并让 spring 或您想要处理异常的任何其他 class。
The retrieve() method in WebClient throws a WebClientResponseException
whenever a response with status code 4xx or 5xx is received.
与 retrieve() 方法不同,exchange() 方法在 4xx 或 5xx 响应的情况下不会抛出异常。您需要自己检查状态码并按照您想要的方式进行处理。
Mono<Object> result = webClient.get().uri(URL).exchange().log().flatMap(entity -> {
HttpStatus statusCode = entity.statusCode();
if (statusCode.is4xxClientError() || statusCode.is5xxServerError())
{
return Mono.error(new Exception(statusCode.toString()));
}
return Mono.just(entity);
}).flatMap(clientResponse -> clientResponse.bodyToMono(JSONObject.class))
参考: https://www.callicoder.com/spring-5-reactive-webclient-webtestclient-examples/
我有3个微服务应用。我正在尝试使用反应包中的 webclient 进行 2 get 异步调用,然后在收到响应时将它们组合起来。
示例代码: (引用自 - https://docs.spring.io/spring/docs/5.1.9.RELEASE/spring-framework-reference/web-reactive.html#webflux-client-synchronous)
Mono<Person> personMono = client.get().uri("/person/{id}", personId)
.retrieve().bodyToMono(Person.class);
Mono<List<Hobby>> hobbiesMono = client.get().uri("/person/{id}/hobbies", personId)
.retrieve().bodyToFlux(Hobby.class).collectList();
Map<String, Object> data = Mono.zip(personMono, hobbiesMono, (person, hobbies) -> {
Map<String, String> map = new LinkedHashMap<>();
map.put("person", personName);
map.put("hobbies", hobbies);
return map;
})
.block();
我的问题是如何向 get 调用添加异常处理?
如何检查我收到的是 404、204 还是其他?
我试过:
- 将 .onStatus() 添加到 GET 调用
.onStatus(HttpStatus::is4xxClientError, clientResponse ->
Mono.error(new Data4xxException(String.format(
"Could not GET data with id: %s from another app, due to error:
%s", key, clientResponse))))
.onStatus(HttpStatus::is5xxServerError, clientResponse ->
Mono.error(new Data5xxException(
String.format("For Data %s, Error Occurred: %s", key, clientResponse))))
- 添加异常处理程序 - 但我确实没有控制器,所以这似乎不起作用。
@ExceptionHandler(WebClientException.class)
public Exception handlerWebClientException(WebClientException webClientException) {
return new Data4xxException("Testing", webClientException);
}
- 添加了一个 class,其中包含 ControllerAdvice 和 ExceptionHandler
@ControllerAdvice
public class WebFluxExceptionHandler {
@ExceptionHandler(WebClientException.class)
public Exception handlerWebClientException(WebClientException webClientException) {
return new Data4xxException("Testing", webClientException);
}
}
但我没有在 spring-boot 日志中看到它们。
Mono.zip.block() 方法只是 return 为 null,实际上并没有抛出任何异常。
如何让 zip 方法抛出异常而不是 return null?
你问的不是很清楚
“如何让 zip 方法抛出异常而不是 return null?”
在 WebFlux 中,您通常不会抛出异常,而是传播异常然后处理它们。为什么?因为我们正在处理数据流,如果你抛出异常,流结束,客户端断开连接,事件链停止。
我们仍然希望维护数据流并处理流经的不良数据。
您可以使用 doOnError
方法处理错误。
.onStatus(HttpStatus::is4xxClientError, clientResponse ->
Mono.error(new Data4xxException(String.format(
"Could not GET data with id: %s from another app, due to error:
%s", key, clientResponse))))
Mono.zip( .. ).doOnError( //Handle your error, log or whatever )
如果你想做一些更具体的事情,你必须用你希望如何处理你的错误来更新你的问题。
方法是按以下方式使用 onErrorMap:
Mono<Person> personMono = client.get()
.uri("/person/{id}", personId)
.retrieve()
.bodyToMono(Person.class)
.onErrorMap((Throwable error) -> error);
onErrorMap
将使 Mono 在 Zip 阻塞时实际抛出错误,终止 zip 并让 spring 或您想要处理异常的任何其他 class。
The retrieve() method in WebClient throws a WebClientResponseException whenever a response with status code 4xx or 5xx is received.
与 retrieve() 方法不同,exchange() 方法在 4xx 或 5xx 响应的情况下不会抛出异常。您需要自己检查状态码并按照您想要的方式进行处理。
Mono<Object> result = webClient.get().uri(URL).exchange().log().flatMap(entity -> {
HttpStatus statusCode = entity.statusCode();
if (statusCode.is4xxClientError() || statusCode.is5xxServerError())
{
return Mono.error(new Exception(statusCode.toString()));
}
return Mono.just(entity);
}).flatMap(clientResponse -> clientResponse.bodyToMono(JSONObject.class))
参考: https://www.callicoder.com/spring-5-reactive-webclient-webtestclient-examples/