Русский
Русский
English
Статистика
Реклама

Перевод Шпаргалка по Spring Boot WebClient

В преддверии старта курса Разработчик на 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 и таймауты.

@Configurationpublic 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 WebClient

WebClient поддерживает методы: get(), post(), put(), patch(), delete(), options() и head().

Также можно указать следующие параметры:

  • Переменные пути (path variables) и параметры запроса с помощью метода uri().

  • Заголовки запроса с помощью метода headers().

  • Куки с помощью метода cookies().

После указания параметров можно выполнить запрос с помощью retrieve() или exchange(). Далее мы преобразуем результат в Mono с помощью bodyToMono() или во Flux с помощью bodyToFlux().

Асинхронный запрос

Давайте создадим сервис, который использует бин WebClient и создает асинхронный запрос.

@Service@AllArgsConstructorpublic 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
К списку статей
Опубликовано: 09.02.2021 02:22:17
0

Сейчас читают

Комментариев (0)
Имя
Электронная почта

Блог компании otus

Программирование

Java

Spring

Webclient

Spring boot

Asynchronous

Reactive programming

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru