在错误情况下从 WebClient 获取响应 body 的正确方法是什么?

What's the correct way to get the response body from a WebClient in an error case?

我是 WebClient 和响应式编程的新手。我想从请求中获得响应 body。如果出现错误,必须记录 http-code、headers 和 body,但仍应返回 body。

经过大量挖掘和谷歌搜索,我找到了两个解决方案。但对我来说,两者看起来都很复杂。有更简单的解决方案吗?

使用 Mono 我找到了这个解决方案:

public Mono<String> log(ProtocolLine protocolLine) {
    return webClient.post()
            .uri("/log")
            .body(BodyInserters.fromObject(protocolLine))
            .exchange()
            .flatMap(clientResponse -> {
                Mono<String> stringMono = clientResponse.bodyToMono(String.class);
                CompletableFuture<String> stringCompleteFuture = new CompletableFuture<String>();
                Mono<String> bodyCompletedMono = Mono.fromFuture(stringCompleteFuture);
                if (clientResponse.statusCode().isError()) {
                    stringMono.subscribe(bodyString -> {
                        LOGGER.error("HttpStatusCode = {}", clientResponse.statusCode());
                        LOGGER.error("HttpHeaders = {}", clientResponse.headers().asHttpHeaders());
                        LOGGER.error("ResponseBody = {}", bodyString);
                        stringCompleteFuture.complete(bodyString);
                    });
                }

                return bodyCompletedMono;
            });
}

基于Flux,它需要更少的代码。但我认为如果我知道只有一个结果,我不应该使用 Flux。

public Flux<String> log(ProtocolLine protocolLine) {
    return webClient.post()
            .uri("/log")
            .body(BodyInserters.fromObject(protocolLine))
            .exchange()
            .flux()
            .flatMap(clientResponse -> {
                Flux<String> stringFlux = clientResponse.bodyToFlux(String.class).share();
                if (clientResponse.statusCode().isError()) {
                    stringFlux.subscribe(bodyString -> {
                        LOGGER.error("HttpStatusCode = {}", clientResponse.statusCode());
                        LOGGER.error("HttpHeaders = {}", clientResponse.headers().asHttpHeaders());
                        LOGGER.error("ResponseBody = {}", bodyString);
                    });
                }

                return stringFlux;
            });
}

这两种解决方案都很丑陋且错误。你几乎不应该在反应管道的中间订阅。订阅者通常是调用客户端,而不是您自己的应用程序。

    public Mono<String> log(ProtocolLine protocolLine) {
    return webClient.post()
            .uri("/log")
            .body(BodyInserters.fromObject(protocolLine))
            .exchange()
            .flatMap(clientResponse -> clientResponse.bodyToMono(String.class)
                .doOnSuccess(body -> {
                    if (clientResponse.statusCode().isError()) {
                        log.error("HttpStatusCode = {}", clientResponse.statusCode());
                        log.error("HttpHeaders = {}", clientResponse.headers().asHttpHeaders());
                        log.error("ResponseBody = {}", body);
                    }
            }));
}

这里可以看到思维方式。我们总是把我们的 clientResponse 和它的主体映射到一个字符串。然后我们 doOnSuccess 当这个 Mono 被订阅者(我们的调用客户端)消费时,检查状态代码是否有错误,如果是这种情况我们记录。

doOnSuccess 方法 returns void 所以它不会 "consume" 单声道或任何东西,它只是在这个 Mono 说它时触发一些东西 "has something in itself",当"done"这么说的时候。

这可以以相同的方式与 Flux 一起使用。