«1. Обзор

Часто полезно использовать код состояния из ответа HTTP, чтобы определить, что приложение должно делать дальше с данным ответом.

В этом руководстве мы рассмотрим, как получить доступ к коду состояния и телу ответа, возвращенному из запроса REST, с помощью WebClient WebFlux.

WebClient был представлен в Spring 5 и может использоваться для асинхронного ввода-вывода при вызове служб RESTful.

2. Пример использования

При выполнении вызовов RESTful к другим службам приложения обычно используют возвращенный код состояния для запуска различных функций. Типичные варианты использования включают изящную обработку ошибок, инициирование повторных попыток запроса и определение ошибки пользователя.

Таким образом, при вызовах REST часто недостаточно просто получить код ответа. Иногда нам также нужно тело ответа.

В следующих примерах давайте посмотрим, как мы можем анализировать тело ответа от клиента REST WebClient. Мы свяжем наше поведение с возвращенным кодом состояния и будем использовать два метода извлечения кода состояния, предоставляемые WebClient: onStatus и ExchangeFilterFunction.

3. Использование onStatus

onStatus — это встроенный механизм, который можно использовать для обработки ответа WebClient. Это позволяет нам применять детализированную функциональность на основе конкретных ответов (например, 400, 500, 503 и т. д.) или категорий статусов (например, 4XX, 5XX и т. д.):

WebClient
  .builder()
  .build()
  .post()
  .uri("/some-resource")
  .retrieve()
  .onStatus(
    HttpStatus.INTERNAL_SERVER_ERROR::equals,
    response -> response.bodyToMono(String.class).map(Exception::new))

Метод onStatus требует два параметра. Первый — это предикат, который принимает код состояния. Выполнение второго параметра основано на выводе первого. Вторая — это функция, которая сопоставляет ответ с Mono или Exception.

В этом случае, если мы увидим INTERNAL_SERVER_ERROR (т. е. 500), мы возьмем тело с помощью bodyToMono, а затем сопоставим его с новым исключением.

Мы можем сцепить вызовы onStatus, чтобы обеспечить функциональность для различных состояний состояния:

Mono<String> response = WebClient
  .builder()
  .build()
  .post()
  .uri("some-resource")
  .retrieve()
  .onStatus( 
    HttpStatus.INTERNAL_SERVER_ERROR::equals,
    response -> response.bodyToMono(String.class).map(ServerErrorException::new)) 
  .onStatus(
    HttpStatus.BAD_REQUEST::equals,
    response -> response.bodyToMono(String.class).map(BadRequestException::new))
  ... 
  .bodyToMono(String.class);

// do something with response

Хотя в нашем примере вызовы onStatus сопоставляются с исключениями, это гибко принимает любой тип.

4. Использование ExchangeFilterFunction

ExchangeFilterFunction — это еще один способ обработки определенных кодов состояния и получения тела ответа. В отличие от onStatus, фильтр обмена является гибким и применяется к функциональным возможностям фильтра на основе любого логического выражения.

Мы можем извлечь выгоду из гибкости функции ExchangeFilterFunction для охвата тех же категорий, что и функция onStatus.

Сначала мы определим метод для обработки возвращенной логики на основе кода состояния, полученного от ClientResponse:

private static Mono<ClientResponse> exchangeFilterResponseProcessor(ClientResponse response) {
    HttpStatus status = response.statusCode();
    if (HttpStatus.INTERNAL_SERVER_ERROR.equals(status)) {
        return response.bodyToMono(String.class)
          .flatMap(body -> Mono.error(new ServerErrorException(body)));
    }
    if (HttpStatus.BAD_REQUEST.equals(status)) {
        return response.bodyToMono(String.class)
          .flatMap(body -> Mono.error(new BadRequestException(body)));
    }
    return Mono.just(response);
}

Затем мы определим фильтр и используем ссылку на метод для нашего обработчика: ~~ ~

ExchangeFilterFunction errorResponseFilter = ExchangeFilterFunction
  .ofResponseProcessor(WebClientStatusCodeHandler::exchangeFilterResponseProcessor);

Подобно вызовам onStatus, мы сопоставляем Exception. Однако использование Mono.error завершит это исключение в ReactiveException. Эту вложенность следует учитывать при обработке ошибки.

Теперь давайте применим это к экземпляру WebClient, чтобы добиться того же эффекта, что и связанный вызов onStatus:

Mono<String> response = WebClient
  .builder()
  .filter(errorResponseFilter)
  .build()
  .post()
  .uri("some-resource")
  .retrieve()
  .bodyToMono(String.class);

// do something with response

5. Заключение

В этой статье мы рассмотрели несколько методов. чтобы получить тело ответа на основе заголовка состояния HTTP. Основываясь на коде состояния, метод onStatus позволяет нам подключать определенные функции. Кроме того, мы можем использовать метод фильтра, чтобы подключить метод общего назначения для обработки постобработки всех ответов.

Как всегда, весь код этого руководства можно найти на GitHub.