如何使用 Spring Boot 对 WebFlux 进行异常处理?
Posted
技术标签:
【中文标题】如何使用 Spring Boot 对 WebFlux 进行异常处理?【英文标题】:How to do Exception Handling for WebFlux using Springboot? 【发布时间】:2020-01-20 11:50:23 【问题描述】:我有 3 个微服务应用程序。我正在尝试使用响应包中的 webclient 进行 2 获取异步调用,然后在收到响应时将它们组合起来。
示例代码: (引自-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);
-
添加了一个包含 ControllerAdvice 和 ExceptionHandler 的类
@ControllerAdvice
public class WebFluxExceptionHandler
@ExceptionHandler(WebClientException.class)
public Exception handlerWebClientException(WebClientException webClientException)
return new Data4xxException("Testing", webClientException);
但我没有看到它们打印在 spring-boot 日志中。
Mono.zip.block() 方法只是返回 null,实际上并没有抛出任何异常。
如何让zip方法抛出异常而不返回null?
【问题讨论】:
什么是 Data4xxException?它是自定义异常吗?你说你没有控制器那么你如何调用你的代码? 是的,抱歉忘了提,这是一个自定义异常。我的代码是一个 springboot 应用程序,它使用来自 JMS 的数据,所以我有监听器。 【参考方案1】:你问的时候不是很清楚
“如何让zip方法抛出异常而不返回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 )
如果你想做一些更具体的事情,你必须用你希望如何处理你的错误来更新你的问题。
【讨论】:
谢谢,虽然这给了我一个提示......正如我提到的,我想抛出一个异常,以便在需要时其他一些类可以处理该异常,否则我可以丢弃整个事务。无论如何,经过多次试错,我想出了一个办法。【参考方案2】:做到这一点的方法是通过以下方式使用onErrorMap:
Mono<Person> personMono = client.get()
.uri("/person/id", personId)
.retrieve()
.bodyToMono(Person.class)
.onErrorMap((Throwable error) -> error);
onErrorMap
将使 Mono 在 Zip 阻塞、终止 zip 并让 spring 或您想要处理异常的任何其他类时实际抛出错误。
【讨论】:
【参考方案3】:WebClient 中的retrieve() 方法抛出WebClientResponseException 每当收到状态码为 4xx 或 5xx 的响应时。
与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/
【讨论】:
以上是关于如何使用 Spring Boot 对 WebFlux 进行异常处理?的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 spring security 和 spring boot 对 Google 用户进行身份验证,将 mongoDB 作为存储库?
如何使用 Gradle Kotlin DSL 对 Spring Boot 应用程序进行 Dockerize
如何使用 power mock 对 Spring Boot Rest 控制器和异常处理程序进行单元测试