Spring5之WebClient简单使用

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring5之WebClient简单使用相关的知识,希望对你有一定的参考价值。

参考技术A Spring3.0引入了RestTemplate,但是在后来的官方源码中介绍,RestTemplate有可能在未来的版本中被弃用,所谓替代RestTemplate,在Spring5中引入了WebClient作为非阻塞式Reactive Http客户端。

采用阻塞IO模式获取输入数据。每个连接都需要独立的线程,完成数据输入、业务处理、返回。传统阻塞IO模型的问题是,当并发数很大时,就要创建大量线程,占用很大的系统资源。连接创建后,如果当前线程暂时没有数据可读,该线程会阻塞在read操作,造成线程资源浪费。

SpringMVC或Struct等框架都是基于Servlet的,其底层IO模型是阻塞IO模型。Spring社区为了解决SpringMVC的阻塞模型在高并发场景下的性能瓶颈,推出了Spring WebFlux,WebFlux底层实现是久经考验的Netty非阻塞IO通信框架。其实WebClient处理单个HTTP请求的响应时长并不比RestTemplate更快,但是它处理并发的能力更强。 所以响应式非阻塞IO模型的核心意义在于,提高了单位时间内有限资源下的服务请求的并发处理能力,而不是缩短了单个服务请求的响应时长。

uriBuilderFactory:自定义UriBuilderFactory灵活配置使用Url
defaultHeader:为HTTP请求设置Headers请求头
defaultCookie:为HTTP请求设置Cookies
defaultRequest:自定义HttpRequest
filter:为HTTP请求增加客户端过滤器
exchangeStrategies:HTTP读写信息自定义
clientConnector:HTTP客户端连接器设置

使用Mono和Flux接收返回结果,一个Mono对象包含0个或1个元素,而一个Flux对象包含1个或多个元素。

如何从 Spring WebClient 获取响应 json

【中文标题】如何从 Spring WebClient 获取响应 json【英文标题】:How to get response json from Spring WebClient 【发布时间】:2020-10-30 12:39:45 【问题描述】:

我一直在尝试遵循最简单的教程来了解如何使用 WebClient,据我了解,与 RestTemplate 相比,它是下一个最棒的东西。

例如,https://www.baeldung.com/spring-5-webclient#4-getting-a-response

所以当我尝试对应该返回一些 json 的 https://petstore.swagger.io/v2/pet/findByStatus?status=available 做同样的事情时,

WebClient webClient = WebClient.create();
webClient.get().uri("https://petstore.swagger.io/v2/pet/findByStatus?status=available").exchange().block();

我完全不知道如何从生成的DefaultClientResponse 对象着手。到达物理反应体不应该这么复杂,但我跑题了。

如何使用我提供的代码获取响应正文?

【问题讨论】:

“当我尝试同样的事情时”,你不是在做同样的事情吗?您没有对客户响应做任何事情,例如映射它,或者调用 bodyToMono 将主体映射到一个类型... 哇....我想通了 【参考方案1】:

以您目前拥有的形式,并解释行为..

WebClient webClient = WebClient.create();
webClient.get()
         .uri("https://petstore.swagger.io/v2/pet/findByStatus?status=available")
         .exchange()
         .block();

block() 通过内部同步订阅Mono 来启动请求,并返回结果ClientResponse。您也可以通过在exchange() 方法返回的Mono 上调用subscribe() 来异步处理此问题,而不是block()

在当前的形式中,在block() 之后,您现在拥有ClientResponse 对象中有关响应的所有元数据(即来自响应标头),包括成功状态。这并不意味着响应主体已经完成。如果您不关心响应负载,您可以确认成功并保留它。

如果您还想查看响应正文,则需要将响应正文流转换为某个类。此时您可以决定是否要将所有内容读入单个 MonobodyToMono 或读入对象流 (Flux) 和 bodyToFlux,例如在响应是 JSON 数组的情况下可以解析成单独的独立 Java 对象。

但是,在您的情况下,您只想按原样查看 JSON。所以转换为String 就足够了。您只需使用bodyToMono,它会返回一个Mono 对象。

WebClient webClient = WebClient.create();
String responseJson = webClient.get()
                               .uri("https://petstore.swagger.io/v2/pet/findByStatus?status=available")
                               .exchange()
                               .block()
                               .bodyToMono(String.class)
                               .block();

在这里,您使用block() 等待响应负载到达并被解析为String,但您也可以将subscribe 发送到Mono 以在响应完成时接收它。

需要注意的一点是retrieve() 可以用来代替exchange() 来缩短ClientResponse。在这种情况下,您让默认行为处理错误响应。使用exchange() 将所有责任放在应用程序上,以响应ClientResponse 上的错误响应。阅读更多in the Javadoc。 retrieve() 版本如下所示。无需block(),因为您只关心响应数据。

WebClient webClient = WebClient.create();
String responseJson = webClient.get()
                               .uri("https://petstore.swagger.io/v2/pet/findByStatus?status=available")
                               .retrieve()
                               .bodyToMono(String.class)
                               .block();

【讨论】:

【参考方案2】:

这是您向RestTemplate提出请求的方式

String json = new RestTemplate()
    .getForEntity("https://petstore.swagger.io/v2/pet/findByStatus?status=available")
    .getBody();

这是您向requests提出请求的方式

import requests

json = requests.get("https://petstore.swagger.io/v2/pet/findByStatus?status=available")
    .content

这里是您向WebClient提出请求的方式

String json = WebClient.create()
    .get()
    .uri("https://petstore.swagger.io/v2/pet/findByStatus?status=available")
    .exchange()
    .block()
    .bodyToMono(String.class)
    .block();

【讨论】:

因为requests 是用没有类型系统的javascript 编写的,因此getForEntityrequests 本身也是阻塞的,WebClient 默认情况下不是。您在RestTemplaterequest 之间的比较是公平的,因为它们都是阻止客户端。如果您希望进行公平的比较,您应该将WebClient 与返回PromiseFetchAxios 进行比较,您会注意到javascript 也很“丑陋”。您正在将苹果与法拉利进行比较。 因此,如果它们本质上是不同的类别/类型,那么为什么我读到的所有内容都说RestTemplate 正在被WebClient 弃用? @ThomasAndolf 为什么我们要摆脱 spring 阻塞客户端? 因为维护2个客户端没有意义,工作量翻倍,WebClient可以充当RestTemplate,而RestTemplate不能充当WebClient 另外,不需要调用两次block,第一次可以去掉。

以上是关于Spring5之WebClient简单使用的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Spring5 WebClient 进行异步调用

我可以使用从 Spring5 的 WebClient 返回的 Flux 的 block() 方法吗?

使用 ssl 的 Spring 5 WebClient

spring 5 webclient使用指南

Spring5 条件化的Bean装配之Conditional的使用

Spring WebClient 使用简介