如何使用 webflux 端点从 React 生成 json 流

Posted

技术标签:

【中文标题】如何使用 webflux 端点从 React 生成 json 流【英文标题】:how consume webflux end point producing json streams from React 【发布时间】:2019-10-10 08:55:42 【问题描述】:

我想了解如何使用 WebFlux restcontroller 从 React 生成 json 流。我的第一个尝试迫使我解析为 json 的文本 instetad,但我觉得我在做一些奇怪的事情。我决定投入精力阅读示例,我发现一个示例返回 org.reactivestreams.Publisher 而不是返回 WebFlux。

我发现了一个可以帮助我的老话题 (Unable to Consume Webflux Streaming Response in React-Native client),但它没有一个单一的答案。好吧,它驱使我在某个地方读到 React 可能不符合 Reactive 但这是一篇非常古老的帖子。

从网上取一个样本,如果你看一下https://developer.okta.com/blog/2018/09/25/spring-webflux-websockets-react,基本上你会发现:

WebFlux 生成 Json 但不流式传输:

RestController producing MediaType.APPLICATION_JSON_VALUE and returning org.reactivestreams.Publisher<the relevant pojo> 

React 消费 json:

 async componentDidMount() 
    const response = await fetch('relevant url');
    const data = await response.json();
  

我尝试过类似的方法,但有两个显着的区别:

我返回 MediaType.TEXT_EVENT_STREAM_VALUE 因为我相信我应该更喜欢返回流,因为我正在使用无阻塞代码,而不是返回 org.reactivestreams.Publisher 我知道返回 Webflux 更有意义以便正确利用Spring 5+。

我的 Webflux 休息控制器:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import com.mybank.web.model.Loans;
import com.mybank.web.service.LoansService;

import reactor.core.publisher.Flux;

@RestController
public class LoansController 
    @Autowired
    private LoansService loansService;

    @CrossOrigin
    @RequestMapping(method = RequestMethod.GET, produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    @ResponseBody
    public Flux<Loans> findAll() 
        Flux<Loans> loans = loansService.findAll();
        return loans;
    

结果显然是一个流,但不是 json(从 Postman 复制):

data:"timestamp":1558126555269,"result":"8"

data:"timestamp":1558132444247,"result":"10"

data:"timestamp":1558132477916,"result":"10"

data:"timestamp":1558132596327,"result":"14"

堆栈基于 MongoDb 和 Spring boot,但我不打算在这里粘贴,因为它不相关。

反应

  async componentDidMount() 
    const response = await fetch('http://localhost:8080');
    const data = await response.json();
  

我收到“Uncaught (in promise) SyntaxError: Unexpected token d in JSON at position 0”,原因很明显:我没有返回有效的 json。

然后,如果我将 Webflux 控制器更改为生成 json(生成 = MediaType.APPLICATION_JSON_VALUE),其余控制器对我的回答既不是流也不是非阻塞代码的结果。

[
    
        "timestamp": 1558126555269,
        "result": "8"
    ,
    
        "timestamp": 1558132444247,
        "result": "10"
    ,
    
        "timestamp": 1558132477916,
        "result": "10"
    ,
    
        "timestamp": 1558132596327,
        "result": "14"
    
]

有了这样的答案,React 前端可以解析,但我知道我没有利用流式传输。有人可能会争辩说,这样简单的例子在流式传输中毫无价值,但我的学习目的实际上是集中在使用 webflux + react 消费流正确理解和编码。

最后,在阅读https://spring.io/blog/2017/02/23/spring-framework-5-0-m5-update 之后,我将 webflux restcontroller 更改为生成 APPLICATION_STREAM_JSON_VALUE,并且我尝试了生成 =“application/stream+json”。对于这两种尝试,restcontroller 的答案似乎是一个 json 流,但 React 再次抱怨:

"timestamp":1558126555269,"result":"8"
"timestamp":1558132444247,"result":"10"
"timestamp":1558132477916,"result":"10"
"timestamp":1558132596327,"result":"14"

React 端出错:

Unexpected token  in JSON at position 41

总结一下:我没有发现如何从 React 消费一个 Webflux Restcontroller 产生 Json 流。

也许一个可能的答案是“通过使用这个库来改变 React 并以这种方式使用”或者答案是“编码 Webflux Restcontroller 以返回该格式”。顺便说一句,在阅读了我在 React + Webflux 主题下找到的所有示例后,我被卡住了。

换句话说,我的直接问题是:?

【问题讨论】:

【参考方案1】:

您是否尝试过使用 EventSource 来消费流? 有点晚回复,但希望它可以帮助你。

const source = new EventSource('/api-stream-endpoint');

source.onmessage = function logEvents(event) 
      console.log(JSON.parse(data));

【讨论】:

【参考方案2】:

服务器正在按预期生成单个 JSON 字符串流;但是,客户端在一个请求中接收它们。

也就是说,客户端正在尝试解析 id: 1 id: 2 并在第二个 上失败。

如果您将服务器切换为生成APPLICATION_JSON,服务器将收到集合[ id: 1 , id:2 ],它应该可以工作。

【讨论】:

以上是关于如何使用 webflux 端点从 React 生成 json 流的主要内容,如果未能解决你的问题,请参考以下文章

如何让 webflux 端点用每个项目的对象名称序列化 json

如何在 spring-webflux RouterFunction 端点中使用 OpenApi 注释?

如何在 spring-mvc 中将日志记录添加到 webflux 端点?

将消息推送到谷歌云发布订阅后如何返回对 webflux 端点的响应?

将错误消息从 SSE (Webflux) Spring Boot 应用程序传递到 Angular 7 前端

禁用执行器健康端点的 webflux(反应式)安全性