Using HTML5 Server-Sent Events with JSON and ASP.NET MVC

April 10, 2012

Using HTML5 Server-Sent Events with JSON and ASP.NET MVC

I’m getting ready for a session that I’m delivering at the end of this month. I wanted to create a sample for using HTML5 Server-Sent Events and decided to share that sample with my Blog’s readers. But before I write about the sample, lets get to know the Server-Sent Events HTML5 API.

Server-Sent Events

Server-Sent Events is an acronym to the HTML5 EventSource JavaScript API. The Server-Sent Events enable servers to push data over HTTP using push protocols. The data that is sent to the client-side must have the text/event-stream MIME type and must be sent in the following form:

data: This is the first message.

Pay attention that the format has to include the data: prefix and end with line break to end the message.

Another way to send data is using a dedicated event by using the event type format. The following example shows two different events that the client-side can listen for:

event: event1
data: event1 data
 
event: event2
data: event2 data

In order to listen to an event in the client-side all you have to do is to create an EventSource object and give it a URL to listen. Also, you need to wire the message event handler in order to handle the data arriving from the server. Here is a simple example of creating an EventSource object:

var source = new EventSource(url);
source.onmessage = function (e) {
    console.log(e.data);
}

The default event source is the message event as shown in the previous example. If you want to listen for a specific event type you will need to wire a handler for that event type like in the following example which listen on the event1 event:

var source = new EventSource(url);
source.addEventListener('event1', function (e) {
    console.log(e.data);
}, false);

Server-Sent Events Using JSON and ASP.NET MVC

After we learned what are Server-Sent Events lets dive into a much more robust example. In the example, I use ASP.NET MVC as my server side technology.

The Client-Side

We start in the client-side implementation. In the Index view of the Home controller I’ll add an messages unordered list and the script that creates the EventSource object.

@{
    ViewBag.Title = "Home Page";
}
<script>
   1:  
   2:     function contentLoaded() {
   3:         var source = new EventSource('home/message');
   4:         var ul = document.getElementById("messages");
   5:         source.onmessage = function (e) {
   6:             var li = document.createElement("li");
   7:             var returnedItem = JSON.parse(e.data)
   8:             li.textContent = returnedItem.message + ' ' + returnedItem.item;
   9:             ul.appendChild(li);
  10:         }
  11:     }
  12:  
  13:     window.addEventListener("DOMContentLoaded", contentLoaded, false);

</script>

<h2>@ViewBag.Message</h2>
<p>
    To learn more about ASP.NET MVC visit <a href="http://asp.net/mvc" title="ASP.NET MVC Website">
        http://asp.net/mvc</a>.
</p>
<ul id="messages">
</ul>

As you can see I use the DOMContentLoaded event to start the script when all the DOM content finished to load. I’m creating an EventSource with the name source to listen to the home/message endpoint. Since I expect that the data returned from the server is in JSON format, I parse it using the JSON.parse function and use its content to create a new list item.

The Server-Side

Writing the client-side was easy. The server side is also easy but there are some things to notice. First of all, I use a BlockingCollection object which is a wrapper on top of a IProducerConsumerCollection collection. Another object that I use is the JavaScriptSerializer that will serialize an object on the server to JSON representation.

Here is the HomeController code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Collections.Concurrent;
using System.Text;
using System.Threading;
using System.Web.Script.Serialization;
 
namespace ServerSentEvents.Controllers
{
  public class HomeController : Controller
  {
    private static BlockingCollection<string> _data = new BlockingCollection<string>();
 
    static HomeController()
    {
      _data.Add("started");
      for (int i = 0; i < 10; i++)
      {
        _data.Add("item" + i.ToString());
      }
      _data.Add("ended");
    }
 
    public ActionResult Index()
    {
      ViewBag.Message = "Welcome to ASP.NET MVC!";
 
      return View();
    }
 
    public ActionResult About()
    {
      return View();
    }
 
    public ActionResult Message() 
    {
      var result = string.Empty;
      var sb = new StringBuilder();
      if (_data.TryTake(out result, TimeSpan.FromMilliseconds(1000)))
      {
        JavaScriptSerializer ser = new JavaScriptSerializer();
        var serializedObject = ser.Serialize(new { item = result, message = "hello" });
        sb.AppendFormat("data: {0}\n\n", serializedObject);
      }
      return Content(sb.ToString(), "text/event-stream");
    }
  }
}

Pay attention that in the Message action I’m returning the text/event-stream content type.

The following figure shows the output of running the above code:

Using HTML5 Server-Sent Events with JSON Objects

Summary

Server-Sent Events is a good mechanism to push data from the server to the client without the need server polling. The API handles only one side communication (server to client). If you want to use a bidirectional communication channel you should look at the HTML5 Web Sockets API. Using Server-Sent Events is appropriate for listening for changes in feeds such as stocks feed or RSS feed as long as they implement the push protocol.

Add comment
facebook linkedin twitter email

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

5 comments

  1. HaiderJune 6, 2012 ב 7:20

    It is possible to send data to specific clients

    Reply
  2. Gil FinkJune 6, 2012 ב 9:23

    @Haider,

    Server-sent events is a client-side JavaScript API. Sending messages to the client is the responsibility of the server-side and is implemented inside of it. If you want that the server will send messages only to a specific client, you will have to implement the behavior in the server.

    Reply
  3. EddoJune 8, 2012 ב 8:02

    using these scripts, the last message which is sent to the client generates the following error:

    EventSource’s response has a MIME type (“text/plain”) that is not “text/event-stream”. Aborting the connection.

    which makes not possible to receive further messages from the server. Is there a way to fix this?

    Reply
  4. LijoJuly 5, 2012 ב 8:05

    In this case Who is requesting?

    Here requset is initiating by client. right?

    Reply
  5. Gil FinkJuly 8, 2012 ב 13:37

    @Lijo and @Eddo,
    Server-sent events are pushed from the server to the client. You wire a listener to an event source on the client side and that handler will listen to events coming from the server.

    Chrome is the only browser currently supporting them and it uses polling for that (instead of waiting for a push notification). This is the cause for the error you see @Eddo.

    Reply