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

时间:2019-06-27 13:59:48

标签: spring-boot reactive-programming spring-webflux spring-webclient

我是WebClient和反应式编程的新手。我想从请求中获取响应主体。如果发生错误,则必须记录http代码,标头和正文,但仍应返回正文。

经过大量的挖掘和谷歌搜索,我找到了两种解决方案。但是,两者对我来说都显得复杂。有没有更简单的解决方案?

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;
            });
}

1 个答案:

答案 0 :(得分:1)

两种解决方案都是丑陋和错误的。您几乎永远不应该在反应式管道的中间进行订阅。订户通常是主叫客户端,而不是您自己的应用程序。

    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,并检查状态码是否有错误,如果是这种情况,我们会记录。

Mono方法返回void,因此它不会“消耗”单声道或其他任何东西,它只会在此Flux说它“本身具有某种东西”,“完成”时触发一些东西。如此一窥。

这可以与NaN相同地使用。