Learn ASP.NET MVC, ASP.NET Core, and Design Patterns through our online training programs. Courses conducted by Bipin Joshi on weekends. Read more details here.

Utilize Server Sent Events in ASP.NET MVC

Some web applications application need to show data in real-time. As soon as the data is made available at the server, it immediately needs to be displayed to the end user. Traditionally developers used to poll the server periodically to check if a new data is available. This approach has its own drawbacks and may prove to be unsuitable in certain cases. Wouldn't it be nice if server notifies the client of new data rather than client checking with the server periodically? That is what HTML5 Server Sent Events (SSE) allow you to do. In this article you will learn what HTML5 Server Sent Events are and how to develop a ASP.NET MVC application that receives real-time data from the server.

Ajax polling vs. SSEs

Web applications that need to display real-time data often use "polling" techniques to fetch the latest data from the server. For example, you may develop an ASP.NET web form that makes Ajax requests to the server periodically to check whether new data is available. Accordingly the web form renders itself showing the latest data. However, such techniques have their own drawbacks including:

  • Polling techniques make too many requests to the web server if the frequency of polling is too short. This causes burden on the server.
  • Since polling operations are performed by the client there is no way to tell the client whether new data is really available on the server. A client may keep polling the server periodically even if there is no data available. This is unnecessary overhead on the overall system.
  • Polling technique is less accurate. Since polling frequency is decided by the client and is independent of server side data availability it may so happen that data is available on the server now but the client is able to show the new data after some time gap.

The Server Sent Events or SSEs are dispatched by the server. Using SSE you can notify the client application whenever something interesting (say availability of new data) happens on the server. The client can then take appropriate action (say displaying the new data to the user).

Example using SSE with ASP.NET MVC Controller

To understand how SSEs can be used in ASP.NET MVC application you will develop an application as shown below:

The application has a view with Start Listening button. Clicking on this button causes the browser to open a connection with a specified Event Source. The event source is responsible to send the notification data back to the browser. Once the even source sends the data the connection to the event source is closed.

Begin by creating a new ASP.NET MVC project and add HomeController to it. In addition to the Index() action, the HomeController will have an action that acts the event source. This action - Process() - is shown below:

public ActionResult Process()
{
    StringBuilder sb = new StringBuilder();
    using (Northwind db = new Northwind())
    {
        foreach (Customer obj in db.Customers)
        {
            string jsonCustomer = JsonConvert.
                                  SerializeObject(obj);
            sb.AppendFormat("data: {0}\n\n", jsonCustomer);
        }
    }
    return Content(sb.ToString(), "text/event-stream");
}

The Process() action creates a StringBuilder for storing the data that is to be sent back to the browser. The for loop simply iterates through the Customers DbSet and grabs an individual Customer object. This Customer object is converted to its JSON representation using Json.Net component's JsonConvert class. The SerializeObject() method accepts a .NET object and returns its JSON equivalent. The JSON data thus obtained is pushed into the StringBuilder object. Notice how the data is pushed into the StringBuilder. It's data followed by a : and then followed by a JSON object. The \n\n at the end indicates end of the data (a single Customer object in this case).

Also notice how the data is returned from the Process() action. The Content() method of the Controller base class takes two parameters - data to be send to the browser and its Content-Type. The data comes from the StringBuilder. The Content-Type is specified as text/event-stream. This content type is required for the proper functioning of SSEs.

This completes the server side code. Let's shift our attention to the client side code.

The Index view consists of a simple form as shown below:

<form>
    <input type="button" id="btnListen" 
           value="Start Listening" />
    <br /><br />
    <div id="headerDiv" class="tickerheading"></div>
    <div id="targetDiv" class="ticker"></div>
    <div id="footerDiv" class="tickerfooter"></div>
</form>

The form contains the Start Listening button and three <div> elements. The headerDiv displays the header message, the footerDiv displays the footer message and the targetDiv displays the data returned by the server.

The jQuery code that implements SSE in the Index view is shown below:

$(document).ready(function () {

    $("#btnListen").click(function () {

        var source = new EventSource('/home/process');

        source.addEventListener("open", function (event) {
            $('#headerDiv').append
             ('<h1>Processing started...</h1>');
        }, false);


        source.addEventListener("error", function (event) {
            if (event.eventPhase == EventSource.CLOSED) {
                $('#footerDiv').append
                 ('<h1>Connection Closed!</h1>');
                source.close();
            }
        }, false);

        source.addEventListener("message", function (event) {
            var data = JSON.parse(event.data);
            $("#targetDiv").append
             ("<div>" + data.CustomerID + 
              " - " + data.CompanyName + "</div>");
        }, false);

    });
});

The code consists of four pieces.

  • In the click event handler an EventSource is created pointing to /home/process. Thus EventSource object will attempt to open a connection to this specified resource.
  • If the connection is opened successfully, the open event is raised by the EventSource object. The open event can be handled using the addEventListener() method. The open event handler simply displays a message in the headerDiv.
  • If there is any error while communicating the error event is raised. Inside, you can check the eventPhase and take appropriate action. In this case the code checks whether the underlying event source has been closed. If so it displays a message in the footerDiv and also calls the close() method of the EventSource. If you don't call the close() method the browser will again trigger the source after three seconds. Calling close() ensures that browser doesn't repeatedly trigger the event source. Of course, if you wish to have such repeated triggering, you shouldn't close the event source.
  • When the server sends event notification to the browser the message event is raised. The message event handler retrieves the data using the event.data property. In this case event.data will be a single Customer object in JSON format. And message will be raised multiple times depending on the data being sent from the server. The CustomerID and CompanyName are then appended to the targetDiv element.

If you run the application, you should get an output similar to the figure shown earlier.

Dealing with lengthy server side processing

So far so good. In the above example, the server side processing was quite straightforward. However, at times the server side processing might be quite lengthy and a lot of data might need to be sent to the client. In such cases, rather than waiting for the computation of all the data it would be better to send data in small chunks (say one Customer at a time). This way the client will receiving something from the server even if the server side processing is yet to complete. If you wish to accomplish such a task you can modify the Process() action like this:

public void Process()
{
    Response.ContentType = "text/event-stream";
    using (Northwind db = new Northwind())
    {
        foreach (Customer obj in db.Customers)
        {
            string jsonCustomer = JsonConvert.
                         SerializeObject(obj);
            string data = $"data: {jsonCustomer}\n\n";
            System.Threading.Thread.Sleep(5000);
            Response.Write(data);
            Response.Flush();
        }
        Response.Close();
    }
}

In this case instead of using the Content() method to return the ActionResult, the code uses Response object. The ContentType property of the Response is set to text/event-stream. A for each loop iterates through all the Customer objects. A Customer is converted into its JSON representation as before and written to the Response stream using Write() method. The Flush() methods of Response ensures that the data is flushed to the browser. The Close() closes the Response stream thus terminating the event source. Note that in the above code a delay of 5000 milliseconds is introduced to mimic some lengthy processing.

If you run the modified application, you will observe that a Customer is displayed after every five seconds instead of displaying all the customers at a time.

That's it for now! Keep coding !!




Bipin Joshi is a software consultant, trainer, author and a yogi having 21+ years of experience in software development. He conducts online courses in ASP.NET MVC / Core, jQuery, AngularJS, and Design Patterns. He is a published author and has authored or co-authored books for Apress and Wrox press. Having embraced Yoga way of life he also teaches Ajapa Meditation to interested individuals. To know more about him click here.

Get connected : Twitter  Facebook  Google+  LinkedIn

Posted On : 02 May 2016



Tags : ASP.NET MVC C# jQuery JavaScript