我可以访问 ExchangeFilterFunction 上的请求/响应正文吗?

jav*_*ats 8 spring reactive-programming spring-webflux

给定一个使用 的交换WebClient,并按自定义过滤ExchangeFilterFunction

@Override
public Mono<ClientResponse> filter(ClientRequest request, ExchangeFunction next) {
    return next.exchange(request)
        .doOnSuccess(response -> {
            // ...
        });
}
Run Code Online (Sandbox Code Playgroud)

尝试使用多次访问响应正文response.bodyToMono()将导致底层 HTTP 客户端连接器抱怨只允许一个接收者。AFAIK,没有办法访问主体的发布者来获取cache()其信号(我不确定这在资源方面是一个好主意),也没有办法以response这样的方式改变或装饰对象:允许访问其主体(就像ServerWebExchange在服务器端一样)。

这是有道理的,但我想知道是否有任何方法可以通过像这样的过滤器形式订阅响应正文的发布者。我的目标是记录给定实例发送/接收的请求/响应WebClient

我是反应式编程的新手,所以如果这里有任何明显的禁忌,请解释一下:)

jus*_*rec 2

仅用于记录,您可以按照此答案HttpClient中的说明添加窃听。

但是,您的问题在日志记录之外的更一般意义上也很有趣。

一种可能的方法是使用前一个请求正文的副本创建实例的副本ClientResponse。这可能违反反应原则,但它为我完成了工作,而且考虑到我的客户中响应机构的规模较小,我认为没有太大的缺点。

就我而言,我需要这样做,因为200 Ok即使请求失败,发送请求的服务器(在我的控制范围之外)也会使用 HTTP 状态。因此,我需要查看响应正文,以找出是否出现问题以及原因是什么。就我而言,如果错误消息表明会话已过期,我会从缓存中逐出请求标头中的会话 cookie。

这些是步骤:

  1. 获取字符串 Mono 形式的响应正文 (cf (1))。
  2. Mono.Error如果检测到错误,则返回 a (cf (2))。
  3. 使用响应正文的字符串构建原始响应的副本 (cf (3))。

您还可以使用对 的依赖项ObjectMapper将字符串解析为对象以进行分析。

请注意,我是用 Kotlin 编写的,但它应该很容易适应 Java。

@Component
class PeekIntoResponseBodyExchangeFilterFunction : ExchangeFilterFunction {
    override fun filter(request: ClientRequest, next: ExchangeFunction): Mono<ClientResponse> {
        return next.exchange(request)
            .flatMap { response ->
                // (1)
                response.bodyToMono<String>()
                    .flatMap { responseBody ->
                        if (responseBody.contains("Error message")) {
                            // (2)
                            Mono.error(RuntimeException("Response contains an error"))
                        } else {
                            // (3)
                            val clonedResponse = response.mutate().body(responseBody).build()
                            Mono.just(clonedResponse)
                        }
                    }
            }
    }
}
Run Code Online (Sandbox Code Playgroud)