Как правильно получить тело ответа от WebClient в случае ошибки?

Я новичок в _ 1_ и реактивное программирование. Я хочу получить тело ответа на запрос. В случае ошибки http-код, заголовки и тело должны быть зарегистрированы, но тело все равно должно быть возвращено.

После долгих поисков и поисков в Google я нашел два решения. Но оба кажутся мне сложными. Есть ли решение попроще?

Оставаясь с 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;
            });
}

person BetaRide    schedule 27.06.2019    source источник


Ответы (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 возвращает void, поэтому он не «потребляет» моно или что-то еще, он просто запускает что-то, когда этот Mono говорит, что он «имеет что-то в себе», когда он «готов», чтобы говорить.

Таким же образом можно использовать Flux.

person Toerktumlare    schedule 27.06.2019
comment
Спасибо за ответ. Могу ли я узнать, как сгенерировать исключение в случае его ошибки. - person Spartan; 24.12.2019
comment
@ThomasAndolf Как бы вы это сделали, если бы использовали retrieve () вместо exchange ()? И успешный ответ и ответ об ошибке содержали разные типы (успех вернет SuccessResponse.class, а ошибка вернет ErrorResponse.class, оба сериализованные в json)? - person npeder; 20.02.2020
comment
вы используете метод onStatus, как показано в официальной документации. docs.spring. io / spring / docs / current / spring-framework-reference / И тогда вы спрашиваете, что вы хотите вернуть 2 разных типа объектов из одной и той же функции. Вы не можете вернуть (например) String или Int из той же функции. Если вы прочитаете документацию по реактору, вы вернете моно-ошибку (содержащую, скажем, исключение или что-то в этом роде), а затем в цепочке у вас будет .doOnError. - person Toerktumlare; 20.02.2020