简体   繁体   中英

how consume webflux end point producing json streams from React

I want to learn how consume WebFlux restcontroller producing streams of json from React. My first tentative forced me to parse to text instetad of json but I have the feeling I am doing something weird. I decided invest energy on reading examples around and I found an example returning org.reactivestreams.Publisher instead of returning a WebFlux.

I found a bit old topic that could help me ( Unable to Consume Webflux Streaming Response in React-Native client ) but it does't have a single answer. Well it drove me to read somewhere that React may not be compliance with Reactive but this was a very old post.

Taking a sample from internet, if you look at https://developer.okta.com/blog/2018/09/25/spring-webflux-websockets-react basically you will find:

WebFlux producing Json BUT NOT STREAMMING:

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

React consuming json:

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

I have tried similar approach but with two significant difference:

I am returning MediaType.TEXT_EVENT_STREAM_VALUE because I believe I should prefer return streams since I am working with no-blocking code and, instead of returning org.reactivestreams.Publisher I understand it makes more sense return Webflux in order to properly take advantage of Spring 5+.

my Webflux rest controller:

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;
    }
}

The outcome is clearly a stream but not a json (copied from Postman):

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

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

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

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

The stack is based on MongoDb and Spring boot but I am not going to paste here since it is not relevant.

React

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

I am getting "Uncaught (in promise) SyntaxError: Unexpected token d in JSON at position 0" for very obvious reason: I am not returning a valid json.

Then if I change the Webflux controller to produce json (produces = MediaType.APPLICATION_JSON_VALUE) the rest controller answer sound to me neither a stream nor a result from a no-blocking code.

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

With such answer the React front end can parse but I understand I am not taking advantage of streamming. Someone may argue that for such simple example is worthless think in streamming but my learning purpose is really focused on understanding and coding properly with webflux + react consumming streams.

Finally, after read https://spring.io/blog/2017/02/23/spring-framework-5-0-m5-update I changed the webflux restcontroller to produce APPLICATION_STREAM_JSON_VALUE and also I tried produces = "application/stream+json". For both tentatives the restcontroller answer seems a stream of json but again the React complains:

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

Error in React side:

Unexpected token { in JSON at position 41

To summarize: I didn't find how consume from React a Webflux Restcontroller producing streams of Json.

Maybe a possible answer is "change React by using this library and consume that way" or maybe the answer is "code Webflux Restcontroller to return in that format". BTW, after read all examples I found under the topic React + Webflux I am stuck.

In other words, my straight question is: how consume webflux end point producing json streams from React?

Have you tried using EventSource to consume the stream? Kind of late reply, but hopefully it can help you.

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

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

The server is producing a stream of individual JSON strings as expected; however, the client is receiving them in one request.

That is, the client is trying to parse { id: 1 } { id: 2 } and failing on the second { .

If you switch the server to produce APPLICATION_JSON , the server will receive the collection [{ id: 1 }, { id:2 }] and it should work.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM