Выбрать главу

Вы можете использовать общий базовый URI для множества разных запросов. В этом случае может быть полезно создать bean-компонент WebClient с базовым URI и внедрить его везде, где это необходимо. Такой bean может быть объявлен так:

@Bean

public WebClient webClient() {

   return WebClient.create("http://localhost:8080");

}

Затем в любом месте, где вам нужно сделать запросы с использованием этого базового URI, WebClient компонент может быть внедрен и использован следующим образом:

@Autowired

WebClient webClient;

public Mono<Ingredient> getIngredientById(String ingredientId) {

   Mono<Ingredient> ingredient = webClient

      .get()

      .uri("/ingredients/{id}", ingredientId)

      .retrieve()

      .bodyToMono(Ingredient.class);

   ingredient.subscribe(i -> { ... })

}

Поскольку WebClient уже был создан, вы можете сразу приступить к работе, вызвав get(). Что касается URI, то при вызове uri() необходимо указать только путь относительно базового URI.

ТАЙМ-АУТ ДЛЯ ДЛИТЕЛЬНЫХ ЗАПРОСОВ

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

Чтобы избежать задержки клиентских запросов медленной сетью или службой, можно использовать метод timeout() из Flux или Mono, чтобы ограничить время ожидания публикации данных. В качестве примера рассмотрим, как можно использовать функцию timeout() при извлечении данных ингредиента:

Flux<Ingredient> ingredients = WebClient.create()

   .get()

   .uri("http://localhost:8080/ingredients")

   .retrieve()

   .bodyToFlux(Ingredient.class);

ingredients

   .timeout(Duration.ofSeconds(1))

   .subscribe(

      i -> { ... },

      e -> {

         // ошибка тайм-аута обработки

})

Как вы можете видеть, прежде чем подписаться на Flux, вы вызвали timeout(), указав продолжительность 1 секунда. Если запрос может быть выполнен менее чем за 1 секунду, то нет никаких проблем. Но если запрос занимает больше 1 секунды, он истекает и вызывается обработчик ошибок, указанный в качестве второго параметра для subscribe().

11.4.2 Отправка ресурсов

Отправка данных с помощью WebClient не сильно отличается от получения данных. В качестве примера предположим, что у вас есть Mono<Ingredient> и вы хотите отправить запрос POST с Ingredient, опубликованным Mono, в URI с относительным путем /ingredients. Все, что вам нужно сделать, это использовать метод post() вместо get() и указать, что Mono будет использоваться для заполнения тела запроса путем вызова body():

Mono<Ingredient> ingredientMono = ...;

Mono<Ingredient> result = webClient

   .post()

   .uri("/ingredients")

   .body(ingredientMono, Ingredient.class)

   .retrieve()

   .bodyToMono(Ingredient.class);

result.subscribe(i -> { ... })

Если у вас нет Mono или Flux для отправки, но вместо этого есть объект домена, вы можете использовать syncBody(). Например, предположим, что вместо Mono<Ingredient> у вас есть Ingredient, который вы хотите отправить в теле запроса:

Ingedient ingredient = ...;

Mono<Ingredient> result = webClient

   .post()

   .uri("/ingredients")

   .syncBody(ingredient)

   .retrieve()

   .bodyToMono(Ingredient.class);

result.subscribe(i -> { ... })

Если вместо запроса POST вы хотите обновить Ingredient с помощью запроса PUT, вы вызываете put() вместо post() и соответственно корректируете путь URI:

Mono<Void> result = webClient

   .put()

   .uri("/ingredients/{id}", ingredient.getId())

   .syncBody(ingredient)

   .retrieve()

   .bodyToMono(Void.class)

   .subscribe();

Запросы PUT обычно имеют пустые полезные нагрузки ответа, поэтому вы должны указать bodyToMono() вернуть Mono типа Void. При подписке на этот Mono, запрос будет отправлен.

11.4.3 Удаление ресурсов

WebClient также позволяет удалять ресурсы с помощью метода delete(). Например, следующий код удаляет ингредиент для переданого ID:

Mono<Void> result = webClient

   .delete()

   .uri("/ingredients/{id}", ingredientId)

   .retrieve()

   .bodyToMono(Void.class)

   .subscribe();

Как и в случае PUT запросов, запросы DELETE обычно не имеют полезной нагрузки. Вы снова возвращаетесь и подписываетесь на Mono<Void>, чтобы отправить запрос.

11.4.4 Обработка ошибок

До сих пор все примеры WebClient предполагали удачное завершение; не было ответов с кодами состояния 400 или 500. Если будет возвращен любой из статусов ошибок, WebClient зарегистрирует ошибку; в противном случае, он будет молча игнорировать это.

Если вам нужно обработать такие ошибки, то вызов onStatus() можно использовать для указания того, как должны обрабатываться различные коды состояния HTTP. onStatus() принимает две функции: функцию предиката, которая используется для соответствия статусу HTTP, и функцию, которая, учитывая объект ClientResponse, возвращает Mono<Throwable>.

Чтобы продемонстрировать, как onStatus() может быть использован для создания пользовательского обработчика ошибок, рассмотрим следующее использование WebClient, целью которого является извлечение ингредиента с учетом его идентификатора: