Spring 5 Reactive 中的 HTTP 响应异常处理

Posted

技术标签:

【中文标题】Spring 5 Reactive 中的 HTTP 响应异常处理【英文标题】:HTTP Response Exception Handling in Spring 5 Reactive 【发布时间】:2018-09-13 20:43:46 【问题描述】:

我正在使用带有 WebFlux 响应式启动器的 Spring Boot 2 和 Spring 5 开发一些响应式微服务。

我面临以下问题:我想处理通过调用另一个 REST 服务接收到的所有 HTTP 状态,并在收到一些错误的 HTTP 状态时引发异常。例如,当我调用一个端点并且我收到一个 404 HTTP 状态时,我想抛出一个异常并在某个 ExceptionHandler 类中处理该异常,就像它在 Spring 4 中使用@ControllerAdvice 的方式一样。

这样做的正确方法是什么?希望能收到一些好的建议。

【问题讨论】:

也可以参考-***.com/questions/43575538/…***.com/questions/49137253/… 也可以参考,***.com/questions/49137253/…***.com/questions/43575538/… 【参考方案1】:

这可以通过两个独立的部分来解决。

如何将 WebClient 收到的 HTTP 404 响应转换为自定义异常

使用WebClient 时,您可以接收来自远程服务的 HTTP 404 响应。默认情况下,所有4xx5xx 客户端响应都将转换为WebClientResponseException。因此,您可以直接在 WebFlux 应用中处理这些异常。

如果您只想将 404 响应转换为自定义异常,您可以执行以下操作:

WebClient webClient = //...
webClient.get().uri("/persons/1")
  .retrieve()
  .onStatus(httpStatus -> HttpStatus.NOT_FOUND.equals(httpStatus),
                        clientResponse -> Mono.error(new MyCustomException()))
  .bodyToMono(...);

这显然是根据每个客户的调用来完成的。

您可以使用ExchangeFilterFunction 以更可重用的方式实现相同的目的,您可以像这样在WebClient 实例上一劳永逸地设置:

WebClient.builder().filter(myExchangeFilterFunction)...

如何在 WebFlux 应用中处理自定义异常

使用带有注释的 Spring WebFlux,您可以使用带有 @ExceptionHandler 注释的方法处理异常(参见 Spring Framework reference documentation)。

注意:使用WebExceptionHandler 是可能的,但它的级别非常低,因为您在那里没有高级支持:您需要手动编写带有缓冲区的响应,而不支持任何序列化。

【讨论】:

很好的答案,但您能否详细说明如何在 exchangeFilterFunction 中获取远程服务的异常消息?我一直试图解决这个问题已经有一段时间了,但没有成功。我认为这会很有帮助,并且会提供更详细的答案。 我不明白 - 您没有从远程服务收到异常消息,您只是收到 HTTP 响应。也许这应该是一个新问题? 哦,是的,对不起。我的意思是,一旦您收到状态码为 4xx 或 5xx 的响应,并且您尝试在 ExchangeFilterFunction 中处理它,您究竟如何才能获得响应的正文(实际上包含错误消息)?正如您所指出的,我想在 WebClient 中处理所有不成功的请求,而不是每个客户端调用。 如何在非 Webflux 应用程序中处理WebClientResponseException?我的理解是 @ExceptionHandler 仅在控制器中受支持。 你能问这个问题吗?在 cmets 中回答并不容易。【参考方案2】:

我认为您正在寻找的是WebFluxResponseStatusExceptionHandler,请查看reference。

在WebHandler API中,可以使用一个WebExceptionHandler来处理 WebFilter 链和目标 WebHandler 的异常。 使用 WebFlux Config 时,注册一个 WebExceptionHandler 就像 就像将它声明为 Spring bean 一样简单,并且可以选择表达 通过 bean 声明上的 @Order 或通过实现优先级 已订购。

This example 可能有帮助,我自己没试过。

@Component
@Order(-2)
class RestWebExceptionHandler implements WebExceptionHandler

    @Override
    public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) 
        if (ex instanceof PostNotFoundException) 
            exchange.getResponse().setStatusCode(HttpStatus.NOT_FOUND);

            // marks the response as complete and forbids writing to it
            return exchange.getResponse().setComplete();
        
        return Mono.error(ex);
    


class PostNotFoundException extends RuntimeException 
    PostNotFoundException(String id) 
        super("Post:" + id + " is not found.");
    

【讨论】:

是的,你说得对。我已经找到了,但我没有看到它的任何实现。你能给我提供类似的东西吗? 我认为这适合我的问题。 @Par Nilsson,感谢这个例子。对于为此苦苦挣扎的人,上面的示例将有助于添加状态代码。如果您还想添加消息,请添加:``` (serverWebExchange, exception) -> exchange.getResponse().setStatusCode(myStatusGivenTheException); byte[] bytes = "一些文本".getBytes(StandardCharsets.UTF_8); DataBuffer 缓冲区 = exchange.getResponse().bufferFactory().wrap(bytes);返回 exchange.getResponse().writeWith(Flux.just(buffer)); ```

以上是关于Spring 5 Reactive 中的 HTTP 响应异常处理的主要内容,如果未能解决你的问题,请参考以下文章

Spring 5:使用 Spring Webflux 开发 Reactive 应用

Reactive Spring 不支持 ServerHttpRequest 作为 REST 端点测试中的参数?

Spring 5 Reactive - WebExceptionHandler 没有被调用

Reactive-Spring-Security-5.1.3.RELEASE,多重授权

在Spring 5中调试Reactive Streams

如何在 Postman 中查看 Spring 5 Reactive API 的响应?