如何从 Spring WebClient 获取响应 json

Posted

技术标签:

【中文标题】如何从 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,第一次可以去掉。

以上是关于如何从 Spring WebClient 获取响应 json的主要内容,如果未能解决你的问题,请参考以下文章

如何记录 Spring WebClient 响应

如何使用 Spring Boot WebClient 收集分页 API 响应?

Spring WebClient - 如何在 HTTP 错误(4xx、5xx)的情况下访问响应正文?

如何在 Spring 响应式 WebClient 中返回 Kotlin Coroutines Flow

如何使用 WebClient 使用响应式 Spring Rest API

如何在运行在 Tomcat 上的 Spring Web 应用程序中使用 Spring 的响应式 WebClient