how to handle json stream issued by spring boot 2 in angular 5
Asked Answered
M

5

15

Spring boot 2 WebFlux generate Json stream in the new version

for example

@GetMapping(value = "stream", produces = APPLICATION_STREAM_JSON_VALUE)
public Flux<Data> stream() {
    return Flux.interval(Duration.ofSeconds(1)).map(Data::new);
}

will produce issuing new data every one second

{"value":"1"}
{"value":"2"}
{"value":"3"}
{"value":"4"}
{"value":"5"}
{"value":"6"}

i have tried angular 5 httpclient

findAll(): Observable<Data> {
   return this._http.get<Data>(this.url);
}

but it not work for me as i want to be reactive it not send me the result as it cache the result until the connection colsed

I want to ask what is the best way to handle this Json in angular 5

Missioner answered 2/3, 2018 at 8:45 Comment(3)
Please read the "how to ask" : stackoverflow.com/help/how-to-ask. Can you tell us what you've already tried ? What would you like to improve in your current way of doing it ?Gertrudgertruda
ok i am sorry i have update the question @GertrudgertrudaMissioner
I think you should use WebSocket instead of HttpClient. Take a look at this tutorial : tutorialedge.net/typescript/angular/angular-websockets-tutorialGertrudgertruda
S
8

As far as I know there is still no official solution (19.08.2018), however I have found some workaround. Each method of a HttpClient has config argument, where you can pass responseType and other things. I have mixed those settings like bellow:

{observe: 'events', responseType: 'text', reportProgress: true}

Then you will receive events with given types, in range 0 to 4. At least in my case type 3 was interesting content, which was in field partialText, but warning - in your case those messages (in partialText field) will look like bellow:

1 message:

{"value":"1"}

2 message:

{"value":"1"}
{"value":"2"}

3 message

{"value":"1"}
{"value":"2"}
{"value":"3"}

etc... so, I have managed it like bellow:

method(url, /*if post - body,*/
      {observe: 'events', responseType: 'text', reportProgress: true})
      .pipe(
        filter(e => e.type === 3 && e.partialText),
        map(e => {
          const partials = e.partialText.trim().split('\n');
          return JSON.parse(partials.pop());
        })
      );
Shout answered 19/8, 2018 at 7:23 Comment(0)
H
3

With Server-Sent Events, it can be done like that:

import * as EventSource from 'eventsource';
...

const eventSource = new EventSource("http://www.example.com/stream");

eventSource.onmessage = (event) => {
  const data = JSON.parse(event['data']);
}
Hydrochloride answered 27/4, 2018 at 20:17 Comment(1)
Make sure to npm install @types/eventsource to avoid TypeScript errorsSunlit
C
2

That is NDJson format. I managed handling it by doing this:

let handled = 0
http.get<any>("http://localhost:9000/myapi", {
    observe: "events",
    reportProgress: true,
    responseType: "text" as "json",
}).pipe(
    filter((e: any) => e.type === HttpEventType.DownloadProgress && e.partialText),
    map(e => e.partialText.trim().split("\n"))
).subscribe((arr) => {
    for (let i = handled; i < arr.length; i++) {
        try {
            console.log(JSON.parse(arr[i])) // Do obs.next(obj) here
            handled = i + 1
        } catch (e) { }
    }
})

You can wrap that code in an Observable and make obs.next(JSON.parse(arr[i])) where the console.log is at.

Coterie answered 7/6, 2022 at 23:42 Comment(0)
M
1

A browser client has no way to consume a JSON stream (application/stream+json) other than using Server-Sent Events or WebSocket.

With requirements and technology you have described, WebSocket is a better fit.

Mellette answered 12/4, 2018 at 17:33 Comment(0)
P
0

Something that I'm thinking, maybe you could keep the open connection (the get/fetch process) in a fire and forget task, and then in another async loop check if there is different content rather than the stored one to save and show it.

Maybe.

Parapodium answered 7/11, 2021 at 16:39 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.