Issue
I have an API that allows a user to run a back end process and get the response as a series of text lines. The controller is calling this:
public async IEnumerable<string> Execute(Info[] infoArray)
{
foreach(var info in infoArray)
{
try
{
var result = await ProcessInfo(info);
yield return result;
}
catch(ProcessException e)
{
yield return e.Message;
}
}
So ProcessInfo
can take some time to execute, but I want the UI to have a running update of the results.
So, basically I want the controller to return a stream, and send one string at a time as it evaluates the IEnumerable
, and then on the Typescript side (I am currently using Axios.get
requests) get it one string at a time so that I can update a GUI with a running log.
I spent several hours digging around on Google and Stackoverflow and couldn't find a good example to follow. What is the best approach?
Solution
Your controller should return an IAsyncEnumerable<string>
or a ContentResult
with a specific media type (text/event-stream
).
You may need to disable request buffering and enable streaming to allow for a continuous flow of data like that
[HttpGet]
[Route("stream")]
public async IAsyncEnumerable<string> StreamData()
{
foreach (var info in infoArray)
{
try
{
var result = await ProcessInfo(info);
yield return $"data: {result}\n\n";
await Task.Delay(1000); // Simulate processing time
}
catch (ProcessException e)
{
yield return $"data: {e.Message}\n\n";
}
}
}
In Angular service or component, create an EventSource
instance that connects to the streaming endpoint of API.
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class StreamService {
private eventSource: EventSource;
startStream(url: string): Observable<string> {
return new Observable(observer => {
this.eventSource = new EventSource(url);
this.eventSource.onmessage = event => {
observer.next(event.data);
};
this.eventSource.onerror = error => {
observer.error(error);
this.eventSource.close();
};
return () => {
this.eventSource.close();
};
});
}
}
Inject the service into component and subscribe to the startStream
method.
Update your component's view as new data arrives
// In your component
this.streamService.startStream('api/stream').subscribe(
data => {
// Update UI here with the incoming data
},
error => {
// Handle errors
}
);
Answered By - 봊 Јìԥ
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.