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!!


Bipin Joshi is an independent software consultant and trainer by profession specializing in Microsoft web development technologies. Having embraced the Yoga way of life he is also a meditation teacher and spiritual guide to his students. He is a prolific author and writes regularly about software development and yoga on his websites. He is programming, meditating, writing, and teaching for over 27 years. To know more about his ASP.NET online courses go here. More details about his Kriya and Meditation online course are available here.

Posted On : 17 November 2021