深圳市甘泉建设监理有限公司网站,青岛网站建设信息公示,沃尔玛超市,深圳外贸网站建设哪家好在 Spring Framework 5.0 及更高版本中#xff0c;RestTemplate 已被弃用#xff0c;取而代之的是较新的 WebClient。这意味着虽然 RestTemplate 仍然可用#xff0c;但鼓励 Spring 开发人员迁移到新项目的 WebClient。
WebClient 优于 RestTemplate 的原因有几个#xff…在 Spring Framework 5.0 及更高版本中RestTemplate 已被弃用取而代之的是较新的 WebClient。这意味着虽然 RestTemplate 仍然可用但鼓励 Spring 开发人员迁移到新项目的 WebClient。
WebClient 优于 RestTemplate 的原因有几个 非阻塞 I/OWebClient 构建在 Reactor 之上它提供了一种非阻塞、反应式的方法来处理 I/O。这可以在高流量应用程序中实现更好的可扩展性和更高的性能。 函数式风格WebClient 使用函数式编程风格可以使代码更易于阅读和理解。它还提供了流畅的 API可以更轻松地配置和自定义请求。 更好地支持流式传输WebClient 支持请求和响应正文的流式传输这对于处理大文件或实时数据非常有用。 改进的错误处理WebClient 提供比 RestTemplate 更好的错误处理和日志记录从而更轻松地诊断和解决问题。
重点即使升级了spring web 6.0.0版本也无法在HttpRequestFactory中设置请求超时这是放弃使用 RestTemplate 的最大因素之一。 设置请求超时不会有任何影响
总的来说虽然 RestTemplate 可能仍然适用于某些用例但 WebClient 提供了几个优势使其成为现代 Spring 应用程序的更好选择。 让我们看看如何在 SpringBoot 3 应用程序中使用 WebClient。 (1) 创建网络客户端
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 实现如下所示的相同功能
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) 异步发送请求
有时我们不想等待响应而是希望异步处理响应这可以按如下方式完成
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 MonoString 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);
}要使用此函数只需传入要向其发送 POST 请求的 URL 以及要在请求正文中以 URL 编码字符串形式发送的数据。该函数将返回来自服务器的响应或者如果请求由于任何原因失败则返回一条错误消息。 请注意在此示例中WebClient是使用默认配置构建的。你可能需要根据不同要求进行不同的配置。 另请注意block()用于同步等待响应这可能并不适合所有情况。你可能需要考虑subscribe()异步使用和处理响应。 要使用响应您可以订阅Mono并异步处理响应。下面是一个例子
makePostRequestAsync( https://example.com/api , param1value1¶m2value2 )
.subscribe(response - { // 处理响应System.out.println ( response );
}, error - { / / 处理错误System.err.println ( error .getMessage ()); }
);subscribe()用于异步处理响应你可以提供两个 lambda 表达式作为 subscribe() 的参数。如果请求成功并收到响应作为参数则执行第一个 lambda 表达式如果请求失败并收到错误作为参数则执行第二个 lambda 表达式。
(4) 处理4XX和5XX错误
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 MonoString 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) 根据错误状态采取行动
要根据Mono的subscribe()方法中的错误采取操作可以在subscribe函数中处理响应的lambda表达式之后添加另一个lambda表达。如果在处理Monumber的过程中出现错误则执行第二个lambda表达式。
下面是如何使用makePostRequestAsync函数和处理subscribe方法中的错误的更新示例:
makePostRequestAsync(https://example.com/api, param1value1param2value2)
.subscribe(response - {// handle the responseSystem.out.println(response);
}, error - {// handle the errorSystem.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);}
});subscribe方法中的第二个lambda表达式检查错误是否是WebClientResponseException的实例这是WebClient在服务器有错误响应时抛出的特定类型的异常。如果它是WebClientResponseException的实例则代码将从异常中提取状态代码和状态文本并将它们记录到日志中。
还可以根据发生的特定错误在此lambda表达式中添加其他错误处理逻辑。例如你可以重试请求、回退到默认值或以特定方式记录错误。
(6) 处理成功响应和错误的完整代码
responseMono.subscribe(
response - {// handle the responseLOG.info(SUCCESS API Response {}, response);
},
error - {// handle the errorLOG.error(An error occurred: {}, error.getMessage());LOG.error(error class: {}, error.getClass());// Errors / Exceptions from Serverif (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);}}}
});超时
我们可以在每个请求中设置超时如下所示
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 调用非阻塞 I/O 调用肯定会提高应用程序性能。它不仅提供了许多其他令人兴奋的功能例如改进的错误处理和对流的支持而且如果需要它还可以在阻塞模式下使用来模拟 RestTemplate 行为。