Выполнение HTTP-запроса с помощью Spring WebClient: GET

RedDeveloper
25.04.2023 08:32
Выполнение HTTP-запроса с помощью Spring WebClient: GET

WebClient - это реактивный веб-клиент, представленный в Spring 5. Это реактивное, неблокирующее решение, работающее по протоколу HTTP/1.1.

WebClient разрабатывается как альтернатива RestTemplate и поддерживает синхронизацию/асинхронизацию и неблокирующие операции. Поскольку RestTemplate находится в режиме обслуживания, нам следует ознакомиться с WebClient.

Чтобы использовать WebClient, нам необходимо добавить зависимость WebFlux.

Сборка с помощью Maven: pom.xml

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-webflux</artifactId>
</dependency>

Сборка с помощью Gradle: build.gradle

dependencies {
    compile 'org.springframework.boot:spring-boot-starter-webflux'
}

Существует три способа создания экземпляра WebClient.

Первый способ создания экземпляра WebClient - это создание с настройками по умолчанию.

WebClient client = WebClient.create();

Второй способ - это создание путем передачи базового URI в качестве параметра. Этот вариант также создает экземпляр WebClient с настройками по умолчанию.

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

Последний способ - создание полностью настраиваемого экземпляра с помощью DefaultWebClientBuilder. Это самый продвинутый способ создания экземпляра WebClient.

WebClient client = WebClient.builder()
  .baseUrl("http://localhost:8080")
  .defaultCookie("cookieKey", "cookieValue")
  .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) 
  .defaultUriVariables(Collections.singletonMap("url", "http://localhost:8080"))
  .build();

Давайте создадим простой проект для лучшего понимания. В этом проекте мы будем получать данные пользователя из публичного api random-data-api .

Чтобы сделать этот проект, нам нужно добавить только две зависимости: Spring Web и WebFlux.

<dependencies>
  <dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-web</artifactId>
  </dependency>

  <dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-test</artifactId>
   <scope>test</scope>
  </dependency>

  <dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-webflux</artifactId>
  </dependency>
 </dependencies>

URI api - это https://random-data-api.com/api/v2/users . Я запишу его в application.properties как API_URI.

Application.properties

API_URI= https://random-data-api.com/api/v2/users

Теперь нам нужно создать боб WebClient, чтобы он использовался Spring.

@Configuration
public class WebClientConfig {

    @Bean
    public WebClient getWebClient(@Value("${API_URI}") String apiURI){
        return WebClient.builder().baseUrl(apiURI).build();
    }
}

Мы не будем использовать все ключи ответа, поэтому нам нужно создать пользовательскую модель User.

public record User(String username,
                   String gender,
                   String first_name,
                   String last_name,
                   String email) {
}

При создании модели обратите внимание на то, чтобы переменные назывались так же, как и в случае с ответом. За исключением случаев, когда необходимо использовать аннотацию JsonProperty.

@JsonIgnoreProperties(ignoreUnknown = true)
public record User(@JsonProperty("username") String un,
                   @JsonProperty("gender") String g,
                   @JsonProperty("first_name") String fn,
                   @JsonProperty("last_name") String ln,
                   @JsonProperty("email") String e) {
}

Чтобы использовать WebClient, нам нужно создать сервис, который будет получать данные из API_URI.

@Service
public class UserService {

    private final WebClient webClient;

    public UserService(WebClient webClient) {
        this.webClient = webClient;
    }

    public Mono<User> GetRandomUserFromWebClient(){
         return webClient.get()
                 .retrieve()
                 .bodyToMono(User.class)
                 .retryWhen(Retry.fixedDelay(2, Duration.ofSeconds(1)))
                 .doOnError(error -> logger.error("There is an error while sending request {}", error.getMessage()))
                 .onErrorResume(error -> Mono.just(
                         new User(null,
                                 null,
                                 null,
                                 null,
                                 null)));
    }
}

Если вы хотите использовать WebClient синхронно, как RestTemplate, вы можете использовать метод .block().

Наконец, мы создадим контроллер для обработки HTTP-запросов.

@RestController
@RequestMapping("api/v1/user")
public class UserController {

    private final UserService userService;

    public UserController(UserService userService) {
        this.userService = userService;
    }

    @GetMapping
    public Mono<User> getRandomUser(){
        return userService.GetRandomUserFromWebClient();
    }
}

Теперь мы готовы к тестированию нашего проекта.

Ответ
Ответ

Мы также можем писать тесты для WebClient, используя WebTestClient. WebTestClient имеет множество мощных методов для тестирования.

class UserServiceTest {
    WebTestClient webTestClient;

    @BeforeEach
    void setUp(){
        webTestClient = WebTestClient.bindToServer().baseUrl("https://random-data-api.com/api/v2/users").build();
    }

    @Test
    void getRandomUserFromWebClient() {
        webTestClient.get()
                .accept(MediaType.APPLICATION_JSON)
                .exchange()
                .expectStatus().isOk()
                .expectBody();
    }
}

В заключение, поскольку RestTemplate находится в режиме обслуживания, WebClient является мощной альтернативой для него. Спасибо, что дочитали до конца. Надеюсь, вам понравилось содержание. Я приветствую вашу критику. Вы можете получить репо на Github с здесь .

Источники, которые очень помогли мне в процессе обучения:

https://www.baeldung.com/spring-5-webclient

Spring WebClient: Yeni Nesil Reactive HTTP Client

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?

20.08.2023 18:21

Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в 2023-2024 годах? Или это полная лажа?".

Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией

20.08.2023 17:46

В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.

Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox

19.08.2023 18:39

Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в частности, магию поплавков и гибкость flexbox.

Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest

19.08.2023 17:22

В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для чтения благодаря своей простоте. Кроме того, мы всегда хотим проверить самые последние возможности в наших проектах!

Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️

18.08.2023 20:33

Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий их языку и культуре.

Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL

14.08.2023 14:49

Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип предназначен для представления неделимого значения.