使用 webflux 处理异常并返回正确的 HTTP 代码

Posted

技术标签:

【中文标题】使用 webflux 处理异常并返回正确的 HTTP 代码【英文标题】:Handling exceptions and returning proper HTTP code with webflux 【发布时间】:2018-07-20 13:55:55 【问题描述】:

我正在使用 WebFlux 的功能端点。我使用 onErrorResume 将服务层发送的异常转换为 HTTP 错误代码:

public Mono<String> serviceReturningMonoError() 
    return Mono.error(new RuntimeException("error"));  


public Mono<ServerResponse> handler(ServerRequest request) 
    return serviceReturningMonoError().flatMap(e -> ok().syncBody(e))
                            .onErrorResume( e -> badRequest(e.getMessage()));

只要服务返回 Mono,它就可以正常工作。如果服务返回 Flux,我该怎么办?

public Flux<String> serviceReturningFluxError() 
    return Flux.error(new RuntimeException("error"));  


public Mono<ServerResponse> handler(ServerRequest request) 
    ???

编辑

我尝试了以下方法,但不幸的是它不起作用。 Flux.error 不由onErrorResume 处理并传播到框架。在 http 响应序列化过程中,当异常被拆箱时,Spring Boot 异常管理会捕获它并将其转换为 500。

public Mono<ServerResponse> myHandler(ServerRequest request) 
      return ok().contentType(APPLICATION_JSON).body( serviceReturningFluxError(), String.class)
             .onErrorResume( exception -> badRequest().build());

我实际上对这种行为感到惊讶,这是一个错误吗?

【问题讨论】:

【参考方案1】:

我找到了解决此问题的另一种方法,在 body 方法中捕获异常并将其映射到 ResponseStatusException

public Mono<ServerResponse> myHandler(ServerRequest request) 
    return ok().contentType(MediaType.APPLICATION_JSON)
            .body( serviceReturningFluxError()
                    .onErrorMap(RuntimeException.class, e -> new ResponseStatusException( BAD_REQUEST, e.getMessage())), String.class);

使用这种方法,Spring 可以正确处理响应并返回预期的 HTTP 错误代码。

【讨论】:

【参考方案2】:

您的第一个示例使用Mono(即最多一个值),因此它与Mono&lt;ServerResponse&gt; 配合得很好-该值将在内存中异步解析,并且根据结果我们将返回不同的响应或处理业务手动异常。

如果是 Flux(即 0..N 个值),则可能在任何给定时间发生错误。

在这种情况下,您可以使用collectList 运算符将您的Flux&lt;String&gt; 转换为Mono&lt;List&lt;String&gt;&gt;,并带有一个很大的警告:所有元素都将缓冲在内存中。如果数据流很重要,或者您的控制器/客户端是否依赖流数据,这不是这里的最佳选择。

恐怕我没有更好的解决方案来解决这个问题,原因如下:由于Flux 期间随时可能发生错误,因此无法保证我们可以更改 HTTP 状态和响应:事情可能已经在网络上刷了。在使用 Spring MVC 并返回 InputStreamResource 时,情况已经如此。

Spring Boot 错误处理功能尝试编写错误页面并更改 HTTP 状态(请参阅ErrorWebExceptionHandler 和实现类),但如果响应已经提交,它将记录错误信息并让您知道 HTTP状态可能是错误的。

【讨论】:

谢谢,很好的解释!很难完全摆脱旧的同步思维 :) 为了解决我的问题,我用其他方法更改了我的服务 API,以便在调用异步调用之前执行这些检查。 我在this post添加了一些例子

以上是关于使用 webflux 处理异常并返回正确的 HTTP 代码的主要内容,如果未能解决你的问题,请参考以下文章

为啥要在 Webflux 应用程序中构建自己的错误/异常处理?

在身份验证 Spring Security + WebFlux 期间抛出和处理自定义异常

WebFlux 功能:如何检测空 Flux 并返回 404?

Spring Boot 2 Webflux的全局异常处理

如何使用 Spring Boot 对 WebFlux 进行异常处理?

Spring-Integration Webflux 异常处理