[Программирование, Java] Шпаргалка по Spring Boot WebClient (перевод)

Автор Сообщение
news_bot ®

Стаж: 6 лет 4 месяца
Сообщений: 27286

Создавать темы news_bot ® написал(а)
09-Фев-2021 03:31
В преддверии старта курса «Разработчик на Spring Framework» подготовили традиционный перевод полезного материала.
Также абсолютно бесплатно предлагаем посмотреть запись демо-урока на тему «Введение в облака, создание кластера в Mongo DB Atlas».

WebClient — это неблокирующий, реактивный клиент для выполнения HTTP-запросов.Время RestTemplate подошло к концуВозможно, вы слышали, что время RestTemplate на исходе. Теперь это указано и в официальной документации:
NOTE: As of 5.0 this class is in maintenance mode, with only minor requests for changes and bugs to be accepted going forward. Please, consider using the org.springframework.web.reactive.client.WebClient which has a more modern API and supports sync, async, and streaming scenarios.ПРИМЕЧАНИЕ: Начиная с версии 5.0, этот класс законсервирован и в дальнейшем будут приниматься только минорные запросы на изменения и на исправления багов. Пожалуйста, подумайте об использовании org.springframework.web.reactive.client.WebClient, который имеет более современный API и поддерживает синхронную, асинхронную и потоковую передачи.
Конечно, мы понимаем, что RestTemplate не исчезнет мгновенно, однако новой функциональности в нем уже не будет. По этой причине в Интернете можно видеть десятки вопросов о том, что такое WebClient и как его использовать. В этой статье вы найдете советы по использованию новой библиотеки. Отличия между WebClient и RestTemplateЕсли в двух словах, то основное различие между этими технологиями заключается в том, что RestTemplate работает синхронно (блокируя), а WebClient работает асинхронно (не блокируя).RestTemplate — это синхронный клиент для выполнения HTTP-запросов, он предоставляет простой API с шаблонным методом поверх базовых HTTP-библиотек, таких как HttpURLConnection (JDK), HttpComponents (Apache) и другими.Spring WebClient — это асинхронный, реактивный клиент для выполнения HTTP-запросов, часть Spring WebFlux.Вам, вероятно, интересно, как можно заменить синхронного клиента на асинхронный. У WebClient есть решение этой задачи. Мы рассмотрим несколько примеров использования WebClient. А сейчас настало время попрощаться с RestTemplate , сказать ему спасибо и продолжить изучение WebClient.Начало работы с WebClientПредварительные условия Подготовка проектаДавайте создадим базовый проект с зависимостями, используя Spring Initializr.
Теперь взглянем на зависимости нашего проекта. Самая важная для нас зависимость —  spring-boot-starter-webflux.
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
Spring WebFlux является частью Spring 5 и обеспечивает поддержку реактивного программирования для веб-приложений.Пришло время настроить WebClient.Настройка WebClientЕсть несколько способов настройки WebClient. Первый и самый простой — создать его с настройками по умолчанию.
WebClient client = WebClient.create();
Можно также указать базовый URL:
WebClient client = WebClient.create("http://base-url.com");
Третий и самый продвинутый вариант — создать WebClient с помощью билдера. Мы будем использовать конфигурацию, которая включает базовый URL и таймауты.
@Configuration
public class WebClientConfiguration {
    private static final String BASE_URL = "https://jsonplaceholder.typicode.com";
    public static final int TIMEOUT = 1000;
    @Bean
    public WebClient webClientWithTimeout() {
        final var tcpClient = TcpClient
                .create()
                .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, TIMEOUT)
                .doOnConnected(connection -> {
                    connection.addHandlerLast(new ReadTimeoutHandler(TIMEOUT, TimeUnit.MILLISECONDS));
                    connection.addHandlerLast(new WriteTimeoutHandler(TIMEOUT, TimeUnit.MILLISECONDS));
                });
        return WebClient.builder()
                .baseUrl(BASE_URL)
                .clientConnector(new ReactorClientHttpConnector(HttpClient.from(tcpClient)))
                .build();
    }
}
Параметры, поддерживаемые WebClient.Builder можно посмотреть здесь.Подготовка запроса с помощью Spring WebClientWebClient поддерживает методы: get(), post(), put(), patch(), delete(), options() и head().Также можно указать следующие параметры:
  • Переменные пути (path variables) и параметры запроса с помощью метода uri().
  • Заголовки запроса с помощью метода headers().
  • Куки с помощью метода cookies().
После указания параметров можно выполнить запрос с помощью retrieve() или exchange(). Далее мы преобразуем результат в Mono с помощью bodyToMono() или во Flux с помощью bodyToFlux().Асинхронный запросДавайте создадим сервис, который использует бин WebClient и создает асинхронный запрос.
@Service
@AllArgsConstructor
public class UserService {
    private final WebClient webClient;
    public Mono<User> getUserByIdAsync(final String id) {
        return webClient
                .get()
                .uri(String.join("", "/users/", id))
                .retrieve()
                .bodyToMono(User.class);
    }
}
Как вы видите, мы не сразу получаем модель User. Вместо User мы получаем Mono-обертку, с которой выполняем различные действия. Давайте подпишемся неблокирующим способом, используя subscribe().
userService
  .getUserByIdAsync("1")
  .subscribe(user -> log.info("Get user async: {}", user));
Выполнение продолжается сразу без блокировки на методе subscribe(), даже если для получения значения будет требоваться некоторое время.Синхронный запросЕсли вам нужен старый добрый синхронный вызов, то это легко сделать с помощью метода block().
public User getUserByIdSync(final String id) {
    return webClient
            .get()
            .uri(String.join("", "/users/", id))
            .retrieve()
            .bodyToMono(User.class)
            .block();
}
Здесь поток блокируется, пока запрос не выполнится. В этом случае мы получаем запрашиваемую модель сразу же после завершения выполнения метода.Повторные попыткиМы все знаем, что сетевой вызов не всегда может быть успешным. Но мы можем перестраховаться и в некоторых случаях выполнить его повторно. Для этого используется метод retryWhen(), который принимает в качестве аргумента класс response.util.retry.Retry.
public User getUserWithRetry(final String id) {
    return webClient
            .get()
            .uri(String.join("", "/users/", id))
            .retrieve()
            .bodyToMono(User.class)
            .retryWhen(Retry.fixedDelay(3, Duration.ofMillis(100)))
            .block();
}
С помощью билдера можно настроить параметры и различные стратегии повтора (например, экспоненциальную). Если вам нужно повторить успешную попытку, то используйте repeatWhen() или repeatWhenEmpty() вместо retryWhen().Обработка ошибокВ случае ошибки, когда повторная попытка не помогает, мы все еще можем контролировать ситуацию с помощью резервного варианта. Доступны следующие методы:
  • doOnError() — срабатывает, когда Mono завершается с ошибкой.
  • onErrorResume() — при возникновении ошибки подписывается на резервного издателя, используя функцию для выбора действия в зависимости от ошибки.
Вы можете использовать эти функции для вызова другого сервиса, выбрасывания исключения, записи в лог или выполнения любого действия в зависимости от ошибки.
public User getUserWithFallback(final String id) {
    return webClient
            .get()
            .uri(String.join("", "/broken-url/", id))
            .retrieve()
            .bodyToMono(User.class)
            .doOnError(error -> log.error("An error has occurred {}", error.getMessage()))
            .onErrorResume(error -> Mono.just(new User()))
            .block();
}
В некоторых ситуациях может быть полезно отреагировать на конкретный код ошибки. Для этого можно использовать метод onStatus().
public User getUserWithErrorHandling(final String id) {
  return webClient
          .get()
          .uri(String.join("", "/broken-url/", id))
          .retrieve()
              .onStatus(HttpStatus::is4xxClientError,
                      error -> Mono.error(new RuntimeException("API not found")))
              .onStatus(HttpStatus::is5xxServerError,
                      error -> Mono.error(new RuntimeException("Server is not responding")))
          .bodyToMono(User.class)
          .block();
}
Клиентские фильтрыДля перехвата и изменения запроса можно настроить фильтры через билдер WebClient .
WebClient.builder()
  .baseUrl(BASE_URL)
  .filter((request, next) -> next
          .exchange(ClientRequest.from(request)
                  .header("foo", "bar")
                  .build()))
  .clientConnector(new ReactorClientHttpConnector(HttpClient.from(tcpClient)))
  .build();
Ниже приведен пример фильтра для базовой аутентификации с помощью статического фабричного метода.
WebClient.builder()
  .baseUrl(BASE_URL)
  .filter(basicAuthentication("user", "password")) // org.springframework.web.reactive.function.client.basicAuthentication()
  .clientConnector(new ReactorClientHttpConnector(HttpClient.from(tcpClient)))
  .build();
ЗаключениеВ этой статье мы узнали, как настроить WebClient и выполнять синхронные и асинхронные HTTP-запросы. Все фрагменты кода, упомянутые в статье, можно найти в GitHub-репозитории. Документацию по Spring WebClient вы можете найти здесь.Подводя итог, мы видим, что WebClient прост в использовании и включает в себя все необходимые функции, необходимые в современном программировании.Удачи с новым Spring WebClient!
Узнать подробнее о курсе «Разработчик на Spring Framework».
Посмотреть запись демо-урока на тему «Введение в облака, создание кластера в Mongo DB Atlas».

===========
Источник:
habr.com
===========

===========
Автор оригинала: Stanislav Vain
===========
Похожие новости: Теги для поиска: #_programmirovanie (Программирование), #_java, #_java, #_spring, #_webclient, #_spring_boot, #_asynchronous, #_reactive_programming, #_blog_kompanii_otus (
Блог компании OTUS
)
, #_programmirovanie (
Программирование
)
, #_java
Профиль  ЛС 
Показать сообщения:     

Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы

Текущее время: 04-Июл 12:28
Часовой пояс: UTC + 5