[Программирование, Java] Шпаргалка по Spring Boot WebClient (перевод)
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
В преддверии старта курса «Разработчик на 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 Boot 2
- JDK 11
Подготовка проектаДавайте создадим базовый проект с зависимостями, используя 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
===========Похожие новости:
- [Data Engineering] Что такое фильтр Блума? (перевод)
- [JavaScript, Node.JS] Управление версиями Node.js и NPM с помощью NVM (перевод)
- [Математика] Новые квантовые алгоритмы, совершившие прорыв в нелинейных уравнениях (перевод)
- [Ненормальное программирование, Разработка веб-сайтов, JavaScript, Программирование] Зачем мы используем addEventListener вместо onclick?
- [JavaScript, API] ExtendScript Работа с файлами
- [JavaScript, API] Extendscript «Hello World!!!»
- [Java, Тестирование веб-сервисов] Приложения с тяжелой наследственностью. Поддержка или реставрация?
- [Программирование, DevOps, Kubernetes] Круглый стол «Нужно ли разработчику знать Kubernetes» 11 февраля
- [Java] Профилирование в продакшене для поиска узких мест на сервере
- [Программирование, Java, Совершенный код, Проектирование и рефакторинг, Kotlin] Свойства против методов
Теги для поиска: #_programmirovanie (Программирование), #_java, #_java, #_spring, #_webclient, #_spring_boot, #_asynchronous, #_reactive_programming, #_blog_kompanii_otus (
Блог компании OTUS
), #_programmirovanie (
Программирование
), #_java
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 17:51
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
В преддверии старта курса «Разработчик на 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 и поддерживает синхронную, асинхронную и потоковую передачи.
Теперь взглянем на зависимости нашего проекта. Самая важная для нас зависимость — spring-boot-starter-webflux. <dependency>
<groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> </dependency> WebClient client = WebClient.create();
WebClient client = WebClient.create("http://base-url.com");
@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(); } }
@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); } } userService
.getUserByIdAsync("1") .subscribe(user -> log.info("Get user async: {}", user)); public User getUserByIdSync(final String id) {
return webClient .get() .uri(String.join("", "/users/", id)) .retrieve() .bodyToMono(User.class) .block(); } 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(); }
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(); } 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.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(); Узнать подробнее о курсе «Разработчик на Spring Framework».
Посмотреть запись демо-урока на тему «Введение в облака, создание кластера в Mongo DB Atlas». =========== Источник: habr.com =========== =========== Автор оригинала: Stanislav Vain ===========Похожие новости:
Блог компании OTUS ), #_programmirovanie ( Программирование ), #_java |
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 17:51
Часовой пояс: UTC + 5