Spring WebClient - Как обрабатывать сценарии ошибок

Мы используем org.springframework.web.reactive.function.client.WebClient с reactor.netty.http.client.HttpClient как часть Spring 5.1.9 для выполнения запросов с использованием метода exchange(). документация по этому методу подчеркивает следующее:

... при использовании exchange () приложение несет ответственность за использование любого содержимого ответа независимо от сценария (успех, ошибка, неожиданные данные и т. д.). Невыполнение этого может вызвать утечку памяти.

Мы используем exchange() довольно просто, но документация по сценариям ошибок мне непонятна, и я хочу быть уверен, что мы правильно высвобождаем ресурсы для всех результатов. По сути, у нас есть реализация блокировки, которая делает запрос и возвращает ResponseEntity независимо от кода ответа:

    try {
        ...
        ClientResponse resp = client.method(method).uri(uri).syncBody(body).exchange().block();
        ResponseEntity<String> entity =  resp.toEntity(String.class).block();
        return entity;
    } catch (Exception e) {
        // log error details, return internal server error
    }

Если я понимаю реализацию, exchange() всегда будет давать нам ответ, если запрос был успешно отправлен, независимо от кода ответа (например, 4xx, 5xx). В этом сценарии нам просто нужно вызвать toEntity(), чтобы получить ответ. Меня беспокоят сценарии ошибок (например, отсутствие ответа, ошибки низкоуровневого соединения и т. Д.). Будет ли вышеуказанная обработка исключений перехватывать все другие сценарии и будет ли какой-либо из них иметь ответ, который необходимо использовать?

Примечание: ClientResponse.releaseBody() был представлен только в версии 5.2.


person user1491636    schedule 19.02.2020    source источник


Ответы (2)


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

В документации сказано:

ПРИМЕЧАНИЕ. При использовании ClientResponse через метод WebClient exchange () необходимо убедиться, что тело потребляется или высвобождается, используя один из следующих методов:

  1. тело (BodyExtractor)
  2. bodyToMono (Класс) или bodyToMono (ParameterizedTypeReference)
  3. bodyToFlux (Класс) или bodyToFlux (ParameterizedTypeReference)
  4. toEntity (класс) или toEntity (ParameterizedTypeReference)
  5. toEntityList (Класс) или toEntityList (ParameterizedTypeReference)
  6. toBodilessEntity ()
  7. releaseBody ()

Вы также можете использовать bodyToMono (Void.class), если не ожидается никакого содержимого ответа. Однако имейте в виду, что соединение будет закрыто, а не помещено обратно в пул, если какой-либо контент все же поступит. В этом отличие от releaseBody (), который потребляет все тело и освобождает любой полученный контент.

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

Вы можете попробовать использовать .retrieve () вместо .exchange () и обрабатывать ошибки по своему усмотрению.

  public Mono<String> someMethod() {

    return webClient.method(method)
      .uri(uri)
      .retrieve()
      .onStatus(
        (HttpStatus::isError), // or the code that you want
        (it -> handleError(it.statusCode().getReasonPhrase())) //handling error request
      )
      .bodyToMono(String.class);


  }

  private Mono<? extends Throwable> handleError(String message) {
    log.error(message);
    return Mono.error(Exception::new);
  }

В этом примере я использовал Exception, но вы можете создать более конкретное исключение, а затем использовать некоторый обработчик исключений для возврата желаемого статуса http. Не рекомендуется использовать блокировку, лучше передать поток вперед.

person Raul Lopes    schedule 14.03.2020

создать несколько классов исключений

Autowired ObjectMapper

Создайте метод, который возвращает Throwable

Создайте собственный класс для Error.

return webClient
                .get()
                .uri(endpoint)
                .retrieve()
                .bodyToMono(Model.class)
                .onErrorMap(WebClientException.class, this::handleHttpClientException);



private Throwable handleHttpClientException(Throwable ex) {
            if (!(ex instanceof WebClientResponseException)) {
                LOG.warn("Got an unexpected error: {}, will rethrow it", ex.toString());
                return ex;
            }
    
            WebClientResponseException wcre = (WebClientResponseException)ex;
    
            switch (wcre.getStatusCode()) {
                case NOT_FOUND -> throw new NotFoundException(getErrorMessage(wcre));
                case BAD_REQUEST -> throw new BadRequestException(getErrorMessage(wcre));
                default -> {
                    LOG.warn("Got a unexpected HTTP error: {}, will rethrow it", wcre.getStatusCode());
                    LOG.warn("Error body: {}", wcre.getResponseBodyAsString());
                    return ex;
                }
            }
        }



private String getErrorMessage(WebClientResponseException ex) {
        try {
            return mapper.readValue(ex.getResponseBodyAsString(), HttpErrorInfo.class).getMessage();
        } catch (IOException ioe) {
            return ex.getMessage();
        }
    }
person Habeeb Okunade    schedule 19.04.2021