Push data to client using ASP.NET Core Web API and Server Sent Events

Server Sent Events or SSE allow you to send push messages from the server to
client. They come handy when you have a lengthy processing going on the server
and want to notify the client from time to time about the status or intermediate
results of the processing. In this article you will learn how SSE can be used to
push data from Web API to the JavaScript client.
Many a times Web API returns data from its Get() action simply by executing
queries against some database. However, that may not be always the case. Suppose
you have a Web API that returns data to the client after certain processing. So,
Web API might generate the data to be sent to the client in the Get() action
itself rather than simply fetching from the database. If this processing is
lengthy, the client will be required to wait till the Get() action returns.
Wouldn't it be nice if you can push the data to the client as and when it gets
generated on the server? That's what the remainder of this article illustrates.
Let's create a new ASP.NET Core Web API project that contains Get() action as
shown below:
[HttpGet]
public async Task Get()
{
string[] data = new string[] {
"Hello World!",
"Hello Galaxy!",
"Hello Universe!"
};
Response.Headers.Add("Content-Type",
"text/event-stream");
for (int i = 0; i < data.Length; i++)
{
await Task.Delay(TimeSpan.FromSeconds(5));
string dataItem = $"data: {data[i]}\n\n";
byte[] dataItemBytes =
ASCIIEncoding.ASCII.GetBytes(dataItem);
await Response.Body.WriteAsync
(dataItemBytes,0,dataItemBytes.Length);
await Response.Body.FlushAsync();
}
}
The Get() action is an asynchronous action and hence returns a Task. Inside,
the code declares an array containing three strings. These strings are the data
items that are supposed to have generated after certain processing. Then comes
the important part. The Content-Type response header is set to
text/event-stream. This is required for SSE and indicates to the client that
response might contain push notifications.
Then a for loop iterates through the string array declared earlier. To
simulate lengthy processing, the code halts the execution for five seconds. This
is done using the Delay() method of Task. In a more realistic situation here you
will have some business processing that generates the data to be pushed to the
client.
Notice how the dataItem string is formatted. It is data: followed by the
actual data to be sent to the client, followed by line feeds. This specific
format is needed by SSE specifications. So, each iteration pushes a string value
to the client. The dataItem string is converted into a byte array because
WriteAsync() method used later expects a byte array. To convert a string to byte
array GetBytes() method of ASCII class is used.
Next, the byte array just created is written to the response stream using
WriteAsync() method. Once the data is written to the response the FlushAsync()
method is called to flush the data to the client.
This completes the server side part. Now you need to invoke the Web API using
client side JavaScript.
So, add an MVC controller and associated view (you can also use Razor Pages)
and write the following JavaScript code to test the working of the Web API.
var source = new EventSource('/api/sse');
source.onopen = function(event) {
document.getElementById("msg").innerHTML
+= "<h2>Opened</h2>";
};
source.onmessage = function (event) {
document.getElementById("msg").innerHTML
+= "<h2>" + event.data + "</h2>";
};
source.onerror = function(event) {
document.getElementById("msg").innerHTML
+= "<h2>Closed</h2>";
source.close();
}
The code first creates an object of EventSource that points to the Web API
created earlier (Web API controller name is SSEController). Then three events of
the EventSource are handled - onopen, onmessage, and onerror. The onopen event
is raised when a connection is opened with the event source. The onmessage event
is raise whenever a push message is received from the server. The onerror event
is raised when there is any error in the communication.
Inside each of these event handlers the code sets an HTML message inside a
<div> element with ID of msg. The onmessage event handler is important because
that is where data returned from the Get() action is outputted on the web page.
The onerror event handler simply closes the event source.
Now, run the /Home/Index action so that Index view is returned to the browser
and the above JavaScript code gets executed. The following figure shows a sample
run of the application.

That's it for now! Keep coding!!