【问题标题】:What's the correct way to get the response body from a WebClient in an error case?在错误情况下从 WebClient 获取响应正文的正确方法是什么?
【发布时间】:2019-11-09 13:48:36
【问题描述】:

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

【问题讨论】:

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


    【解决方案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 并将它的主体映射到一个字符串。然后,当订阅者(我们的调用客户端)使用此 Mono 时,我们 doOnSuccess 并检查状态代码是否有错误,如果是这种情况我们记录。

    doOnSuccess 方法返回 void,因此它不会“消耗”单声道或任何东西,它只是在这个 Mono 说它“本身有东西”时触发一些东西,当它“完成”时,可以说。

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

    【讨论】:

    • 感谢您的回答。如果出现错误,我可以知道如何抛出异常。
    • @ThomasAndolf 如果使用retrieve() 而不是exchange(),你会怎么做?并且成功响应和错误响应包含不同的类型(成功返回SuccessResponse.class,错误返回ErrorResponse.class,都序列化成json)?
    • 您使用onStatus 方法,如官方文档所示。 docs.spring.io/spring/docs/current/spring-framework-reference/… 然后你要求的是你想从同一个函数返回 2 种不同类型的对象。您不能从同一函数返回(例如)String 或 Int。如果您阅读反应器文档,您会返回一个单声道错误(包含 say en 异常或其他内容),然后在链中稍后会出现 .doOnError
    • 在 Spring 5 中,exchange() 已被弃用:是否有另一种方法可以在发生错误时获取正文?使用retrieve() 或exchangeToMono(),我就是搞不定。
    • ExchangeToMono(response -> ...) 给你一个响应,你可以做任何你想做的事
    猜你喜欢
    • 2019-01-19
    • 2020-06-29
    • 2013-02-27
    • 1970-01-01
    • 2011-03-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多