Как вызвать WebClientResponseException при использовании exchange() с Spring WebClient

При использовании метода retrieve() Spring WebClient в сочетании с bodyToMono применяется обработка ошибок по умолчанию (если ответ имеет код состояния 4xx или 5xx, Mono будет содержать исключение WebClientException). В случаях ошибок результирующее исключение WebClientException очень полезно, поскольку оно содержит запрос И ответ, что очень удобно, например, для ведения журнала. Кроме того, я могу очень хорошо реагировать на ошибки.

/*
 * Easy to use retrieve() with default error handling:
 * By default, if the response has status code 4xx or 5xx, the Mono will contain a WebClientException - AWESOME!
 */
public Optional<MyType> getMyType() {
    try {
        MyType result = client
                .get()
                .uri("/myType")
                .retrieve().bodyToMono(MyType.class).block();

        return Optional.ofNullable(result);
    } catch (NotFound e) {
        return Optional.empty();
    }
}

Однако иногда мне нужно знать точный код состояния ответа, чтобы реагировать на него при дальнейшей обработке этого ответа. Единственный метод, который также может получить код состояния, - это вызвать метод exchange() вместо метода retrieve(). К сожалению, в этом случае обработка ошибок по умолчанию не применяется. Кажется, причина этого в том, что вызов bodyToMono() в ClientResponse имеет другую семантику, чем вызов в ResponseSpec.

Я не могу вызвать уже реализованные методы из Spring, чтобы "инициировать" обработку ошибок, потому что все приятные методы скрыты как частные в WebClient.

Даже если бы я попытался "вручную" создать исключение WebClientResponseException, я не могу получить доступ к запросу, чтобы передать исключение.

public String updateMyType() {
    ClientResponse response = client
            .put()
            .uri("/myType")
            .header(CONTENT_TYPE, APPLICATION_JSON_VALUE)
            .body(fromObject(new MyType()))
            .exchange()
            .block();

    if (response.statusCode().equals(HttpStatus.CREATED)) {
        return "OK";
    } else if (response.statusCode().equals(HttpStatus.NO_CONTENT)) {
        return "updated";
    } else {
        byte[] bodyBytes = null; // already implemented by Sping but not accessible
        Charset charset = null; // already implemented by Sping but not accessible
        HttpRequest request = null; // where should I get this from?
        throw WebClientResponseException.create(response.statusCode().value(),
                response.statusCode().getReasonPhrase(),
                response.headers().asHttpHeaders(),
                bodyBytes,
                charset,
                request);
    }
}

Каков наилучший способ "имитировать" то же поведение, что и для метода retrieve()?

2 ответа

Вероятно, это не тот случай, когда на вопрос был дан ответ, но начиная с Spring 5.2, ClientResponse имеет createException() метод, который построит и вернет Mono<WebClientResponseException>.

clientResponse.createException()
              .flatMap(e -> handleException(e))

https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/reactive/function/client/ClientResponse.html

https://github.com/spring-projects/spring-framework/commit/b4207823afa0f48e06d6bbfe9ae8821e389e380f

Я тоже столкнулся с серьезной борьбой с этим.

Мое решение основано на подходе @olan, но моя цель состояла в том, чтобы использовать обработку ошибок, идентичную той, что используется в методе получения(). К сожалению, рекомендации, представленные в Spring Docs, не работают. Мне пришлось прибегнуть к использованию Flatmap(Mono::error), как описано здесь .

Я использую Spring 5.3.3.

Другие вопросы по тегам