Прекратите использовать RestTemplate и узнайте об официально рекомендуемом WebClient!
Прекратите использовать RestTemplate и узнайте об официально рекомендуемом WebClient!

Привет всем, я Букай Чен~

В Spring Framework 5.0 и более поздних версиях RestTemplate устарел в пользу более нового WebClient. Это означает, что, хотя RestTemplate все еще доступен, разработчикам Spring рекомендуется перейти на WebClient для новых проектов.

Есть несколько причин, почему WebClient лучше RestTemplate:

  • Неблокирующий ввод-вывод: WebClient построен на Reactor, который обеспечивает неблокирующий, реактивный подход к обработке ввода-вывода. Это обеспечивает лучшую масштабируемость и более высокую производительность в приложениях с высоким трафиком.
  • функциястиль:WebClient Использование функционального стиля программирования упрощает чтение и понимание вашего кода. Это также обеспечивает плавность API, который упрощает настройку и настройку запросов.
  • Улучшенная поддержка потоковой передачи: WebClient поддерживает потоковую передачу тел запросов и ответов, что полезно для обработки больших файлов или данных в реальном времени.
  • Улучшенная обработка ошибок: WebClient обеспечивает лучшую обработку и ведение журнала ошибок, чем RestTemplate, что упрощает диагностику и решение проблем.

Ключевой момент: даже если весна будет обновлена web В версии 6.0.0 запрос тайм-аут не может быть установлен в существующем HttpRequestFactory, от которого отказались. RestTemplate один из важнейших факторов.

Установка тайм-аута запроса не даст никакого эффекта

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

Давайте посмотрим, как использовать WebClient в приложении SpringBoot 3.

(1) Создайте сетевой клиент:

Язык кода:javascript
копировать
import io.netty.channel.ChannelOption;
import io.netty.channel.ConnectTimeoutException;
import io.netty.handler.timeout.ReadTimeoutException;
import io.netty.handler.timeout.ReadTimeoutHandler;
import io.netty.handler.timeout.TimeoutException;
import jakarta.annotation.PostConstruct;
import java.time.Duration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.reactive.function.client.WebClientRequestException;
import org.springframework.web.reactive.function.client.WebClientResponseException;
import reactor.core.publisher.Mono;
import reactor.netty.http.client.HttpClient;


HttpClient httpClient =
        HttpClient.create()
            .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, connectionTimeout)
            .responseTimeout(Duration.ofMillis(requestTimeout))
            .doOnConnected(conn -> conn.addHandlerLast(new ReadTimeoutHandler(readTimeout)));

   WebClient client =
        WebClient.builder().clientConnector(new ReactorClientHttpConnector(httpClient)).build();

(2) Отправлять запросы синхронно (как RestTemplate)

Если вы хотите придерживаться старого метода отправки HTTP-запроса и ожидания ответа, вы также можете использовать WebClient для достижения той же функциональности, как показано ниже:

Язык кода:javascript
копировать
public String postSynchronously(String url, String requestBody) {
  LOG.info("Going to hit API - URL {} Body {}", url, requestBody);
  String response = "";
  try {
    response =
        client
            .method(HttpMethod.POST)
            .uri(url)
            .accept(MediaType.ALL)
            .contentType(MediaType.APPLICATION_JSON)
            .bodyValue(requestBody)
            .retrieve()
            .bodyToMono(String.class)
            .block();

  } catch (Exception ex) {
    LOG.error("Error while calling API ", ex);
    throw new RunTimeException("XYZ service api error: " + ex.getMessage());
  } finally {
    LOG.info("API Response {}", response);
  }

  return response;
}

Block() используется для синхронного ожидания ответа. Это может подойти не для всех ситуаций. Возможно, вы захотите использовать асинхронную функцию subscribe() и асинхронную обработку ответа.

(3) Отправить запрос асинхронно:

Иногда мы не хотим ждать ответа, а хотим обработать его асинхронно. Это можно сделать следующим образом:

Язык кода:javascript
копировать
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;

public static Mono<String> makePostRequestAsync(String url, String postData) {
    WebClient webClient = WebClient.builder().build();
    return webClient.post()
            .uri(url)
            .contentType(MediaType.APPLICATION_FORM_URLENCODED)
            .body(BodyInserters.fromFormData("data", postData))
            .retrieve()
            .bodyToMono(String.class);
}

Чтобы использовать эту функцию, просто передайте URL-адрес, на который вы хотите отправить запрос POST, и данные для отправки в виде строки в кодировке URL-адреса в теле запроса. Подпишитесь на аккаунт Gongzhong: Колонка технологий Ма Юаня, ответьте на ключевые слова: 1111, чтобы получить внутреннее руководство по настройке производительности Java Alibaba! Эта функция вернет ответ от сервера или сообщение об ошибке, если запрос по какой-либо причине не выполнен.

Обратите внимание, что в этом примере WebClient создан с конфигурацией по умолчанию. Возможно, вам придется настроить его по-разному в зависимости от различных требований. Также обратите внимание, что функция Block() используется для синхронного ожидания ответа, что может подходить не для всех ситуаций. Возможно, вы захотите использовать асинхронную функцию subscribe() и обработать ответ.

Чтобы использовать ответ, вы можете подписаться на Mono и обрабатывать ответ асинхронно. Вот пример:

Язык кода:javascript
копировать
makePostRequestAsync( "https://example.com/api" , "param1=value1¶m2=value2" ) 
.subscribe(response -> { 
    // Обработка ответа
    System.out.println ( response ); 
}, error -> { 
    / / обработка ошибок
    System.err.println ( error .getMessage ());     
    }
);

subscribe() используется для асинхронной обработки ответов. В качестве параметров subscribe() можно указать два лямбда-выражения. Если запрос успешен и в качестве параметра получен ответ, выполняется первое лямбда-выражение; если запрос не выполнен и в качестве параметра получена ошибка, выполняется второе лямбда-выражение.

(4) Обработка ошибок 4XX и 5XX:

Язык кода:javascript
копировать
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;

public static Mono<String> makePostRequestAsync(String url, String postData) {
    WebClient webClient = WebClient.builder()
            .baseUrl(url)
            .build();
    return webClient.post()
            .uri("/")
            .contentType(MediaType.APPLICATION_FORM_URLENCODED)
            .body(BodyInserters.fromFormData("data", postData))
            .retrieve()
            .onStatus(HttpStatus::is4xxClientError, clientResponse -> Mono.error(new RuntimeException("Client error")))
            .onStatus(HttpStatus::is5xxServerError, clientResponse -> Mono.error(new RuntimeException("Server error")))
            .bodyToMono(String.class);
}

В этом примере метод onStatus() вызывается дважды: один раз для ошибок клиента 4xx и один раз для ошибок сервера 5xx. Каждый вызов onStatus() принимает два параметра:

  • aPredicate определяет, соответствует ли код состояния ошибки условию
  • aFunction используется для возврата Mono, который представляет собой информацию об ошибке, передаваемую подписчикам.

Если код состояния соответствует условию, Mono выдает соответствующий код состояния, и цепочка Mono завершается с ошибкой. В этом примере Mono выдаст сообщение об ошибке RuntimeException, указывающее, является ли ошибка ошибкой клиента или сервера.

(5) Примите меры в зависимости от статуса ошибки:

Чтобы принять меры в случае ошибки в методе subscribe() Mono, вы можете добавить еще одно лямбда-выражение после лямбда-выражения, которое обрабатывает ответ в функции подписки. Если при обработке Monnumber возникает ошибка, выполняется второе лямбда-выражение.

Вот обновленный пример того, как использовать функцию makePostRequestAsync и обрабатывать ошибки в методе подписки:

Язык кода:javascript
копировать
makePostRequestAsync("https://example.com/api", "param1=value1&param2=value2")
.subscribe(response -> {
    // handle the response
    System.out.println(response);
}, error -> {
    // handle the error
    System.err.println("An error occurred: " + error.getMessage());
    if (error instanceof WebClientResponseException) {
        WebClientResponseException webClientResponseException = (WebClientResponseException) error;
        int statusCode = webClientResponseException.getStatusCode().value();
        String statusText = webClientResponseException.getStatusText();
        System.err.println("Error status code: " + statusCode);
        System.err.println("Error status text: " + statusText);
    }
});

Второе лямбда-выражение в методе подписки проверяет, является ли ошибка экземпляром исключения WebClientResponseException, которое представляет собой особый тип исключения, создаваемого WebClient, когда сервер отвечает ошибкой. Если это экземпляр WebClientResponseException, код извлечет код состояния и текст состояния из исключения и зарегистрирует их.

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

(6) Полный код для обработки успешных ответов и ошибок:

Язык кода:javascript
копировать
responseMono.subscribe(
response -> {
  // handle the response
  LOG.info("SUCCESS API Response {}", response);
},
error -> {
  // handle the error
  LOG.error("An error occurred: {}", error.getMessage());
  LOG.error("error class: {}", error.getClass());

  // Errors / Exceptions from Server
  if (error instanceof WebClientResponseException) {
    WebClientResponseException webClientResponseException =
        (WebClientResponseException) error;
    int statusCode = webClientResponseException.getStatusCode().value();
    String statusText = webClientResponseException.getStatusText();
    LOG.info("Error status code: {}", statusCode);
    LOG.info("Error status text: {}", statusText);
    if (statusCode >= 400 && statusCode < 500) {
      LOG.info(
          "Error Response body {}", webClientResponseException.getResponseBodyAsString());
    }

    Throwable cause = webClientResponseException.getCause();
    LOG.error("webClientResponseException");
    if (null != cause) {
      LOG.info("Cause {}", cause.getClass());
      if (cause instanceof ReadTimeoutException) {
        LOG.error("ReadTimeout Exception");
      }
      if (cause instanceof TimeoutException) {
        LOG.error("Timeout Exception");
      }
    }
  }

  // Client errors i.e. Timeouts etc - 
  if (error instanceof WebClientRequestException) {
    LOG.error("webClientRequestException");
    WebClientRequestException webClientRequestException =
        (WebClientRequestException) error;
    Throwable cause = webClientRequestException.getCause();
    if (null != cause) {
      LOG.info("Cause {}", cause.getClass());
      if (cause instanceof ReadTimeoutException) {
        LOG.error("ReadTimeout Exception");
      }
      
      if (cause instanceof ConnectTimeoutException) {
        LOG.error("Connect Timeout Exception");
      }
    }
  }
});

тайм-аут

Мы можем установить тайм-аут в каждом запросе следующим образом:

Язык кода:javascript
копировать
return webClient
    .method(this.httpMethod)
    .uri(this.uri)
    .headers(httpHeaders -> httpHeaders.addAll(additionalHeaders))
    .bodyValue(this.requestEntity)
    .retrieve()
    .bodyToMono(responseType)
    .timeout(Duration.ofMillis(readTimeout))  // request timeout for this request
    .block();

Однако мы не можем существовать устанавливать тайм-аут соединения в каждом запросе, это WebClient свойства можно задать только один раз. При необходимости мы всегда можем создать новое соединение с новым значением тайм-аута. Web Экземпляр клиента.

Различия между подключением тайм-аута, чтением тайм-аута и запросом тайм-аута заключаются в следующем:

в заключение

Поскольку RestTemplace устарел, разработчикам следует начать использовать WebClient для вызовов REST, неблокирующие вызовы ввода-вывода определенно улучшат производительность приложения. Он не только предоставляет множество других интересных функций, таких как улучшенная обработка ошибок и поддержка потоков, но его также можно использовать в режиме блокировки для имитации поведения RestTemplate, если это необходимо.

boy illustration
Учебное пособие по Jetpack Compose для начинающих, базовые элементы управления и макет
boy illustration
Код js веб-страницы, фон частицы, код спецэффектов
boy illustration
【новый! Суперподробное】Полное руководство по свойствам компонентов Figma.
boy illustration
🎉Обязательно к прочтению новичкам: полное руководство по написанию мини-программ WeChat с использованием программного обеспечения Cursor.
boy illustration
[Забавный проект Docker] VoceChat — еще одно приложение для мгновенного чата (IM)! Может быть встроен в любую веб-страницу!
boy illustration
Как реализовать переход по странице в HTML (html переходит на указанную страницу)
boy illustration
Как решить проблему зависания и низкой скорости при установке зависимостей с помощью npm. Существуют ли доступные источники npm, которые могут решить эту проблему?
boy illustration
Серия From Zero to Fun: Uni-App WeChat Payment Practice WeChat авторизует вход в систему и украшает страницу заказа, создает интерфейс заказа и инициирует запрос заказа
boy illustration
Серия uni-app: uni.navigateЧтобы передать скачок значения
boy illustration
Апплет WeChat настраивает верхнюю панель навигации и адаптируется к различным моделям.
boy illustration
JS-время конвертации
boy illustration
Обеспечьте бесперебойную работу ChromeDriver 125: советы по решению проблемы chromedriver.exe не найдены
boy illustration
Поле комментария, щелчок мышью, специальные эффекты, js-код
boy illustration
Объект массива перемещения объекта JS
boy illustration
Как открыть разрешение на позиционирование апплета WeChat_Как использовать WeChat для определения местонахождения друзей
boy illustration
Я даю вам два набора из 18 простых в использовании фонов холста Power BI, так что вам больше не придется возиться с цветами!
boy illustration
Получить текущее время в js_Как динамически отображать дату и время в js
boy illustration
Вам необходимо изучить сочетания клавиш vsCode для форматирования и организации кода, чтобы вам больше не приходилось настраивать формат вручную.
boy illustration
У ChatGPT большое обновление. Всего за 45 минут пресс-конференция показывает, что OpenAI сделал еще один шаг вперед.
boy illustration
Copilot облачной разработки — упрощение разработки
boy illustration
Микросборка xChatGPT с низким кодом, создание апплета чат-бота с искусственным интеллектом за пять шагов
boy illustration
CUDA Out of Memory: идеальное решение проблемы нехватки памяти CUDA
boy illustration
Анализ кластеризации отдельных ячеек, который должен освоить каждый&MarkerгенетическийВизуализация
boy illustration
vLLM: мощный инструмент для ускорения вывода ИИ
boy illustration
CodeGeeX: мощный инструмент генерации кода искусственного интеллекта, который можно использовать бесплатно в дополнение к второму пилоту.
boy illustration
Машинное обучение Реальный бой LightGBM + настройка параметров случайного поиска: точность 96,67%
boy illustration
Бесшовная интеграция, мгновенный интеллект [1]: платформа больших моделей Dify-LLM, интеграция без кодирования и встраивание в сторонние системы, более 42 тысяч звезд, чтобы стать свидетелями эксклюзивных интеллектуальных решений.
boy illustration
LM Studio для создания локальных больших моделей
boy illustration
Как определить количество слоев и нейронов скрытых слоев нейронной сети?
boy illustration
[Отслеживание целей] Подробное объяснение ByteTrack и детали кода