Native HTML5 push notifications with ASP.NET Web API and Knockout.js - StrathWeb

Strath

Native HTML5 push notifications with ASP.NET Web API and Knockout.js

Because you don't need websockets for push notifications

The ASP.NET Web API project progresses at a rapid pace, and has already come a long way since the beta release. And as it is OSS now, it’s really great that we all can see the progress being made (thanks, MS!). So today we are going to build a native HTML5 push notifications mechanism over HTTP protocol, utilizing the latest exciting features of Web API (PushStreamContent), modern browsers’ Server-sent Events EventSource API and, of course, Knockout.js.

Few weeks ago Henrik Nielsen on his blog was kind enough to share some updates about the latest feauters available in the ASP.NET Web API. We are going to be using his sample code as the starting point to develop an application utilizing a real HTTP push messaging infrastructure. We’ll use that to build a browser-based multi-person chat, but the principles showed here could be used for pretty much anything that requires pushing content to the client.

Requirements

Now, before we start, let’s make sure we go through a list of requirements to make this work:

the latest ASP.NET Web API build – BETA from Febraury won’t work! so you either have to have your own build done based on the latest source on codeplex, or you neeed to use the nightly packages via NuGet. The examples here use code version from 4th May

a browser which supports Server-sent Events EventSource API. The code here should work in FF 9+, Chrome 17+, Opera 11.6+ and IE10. Nevertheless, there are ways of making it cross browser (i.e. throught the use of XDomainRequest or polyfill plugins etc.) and I’ll come back to this point later on

- Knockout.js, and Knockout Mapping plugin

Building the project

Now that we have set up the ground rules, let’s create our project. It’s gonna be the standrad ASP.NET MVC 4 with Web API template.

First let’s add one Model to our application, called Message.cs.

This will be the model we’ll use for model binding in our chat application. Whenever a use submits a new message for the chat, the controller method will be accepting this Type as the input.

We add a new controller to our application, called ChatController.cs. In here we are going to derive off the code shared by Henrik. Note, he used ConcrrentDictionary to build the push functionality, but I think ConcurrentQueue would suffice.

Our controller will expose two points of communication – Get() and Set(). Get will be used by clients to subscribe to the EvenSource stream, while Post will be used to send chat messages.

When the user makes a GET request, we’ll create a new HttpResponseMessage using PushStreamContent object and text/event-stream content type. PushStreamContent takes an Action<Stream, HttpContentHeaders, TransportContext> onStreamAvailable parameter in the constructor, and that in turn allows us to manipulate the response stream.

When the user makes a POST request, using model binidng we pull a Message object out of the request and pass it off to MessageCallback.

Let’s take it a step further:

OnStreamAvailable would instantiate a new StreamWriter for every new subscriber (every user of the ChatController). It will then queue the StreamWriter in the local private static property in the ChatController, streammessage. It will be shared accross all instances of the controller and allow us to write a new notification (push them) to every subscriber (or rather, every streamWriter corresponding to a subscriber).

We do that by calliing the void method MessageCallback. As we saw before, it will be called any time a POST request with incoming Message reaches the controller. What happens inside the method is that we iterate through all StreamWriters in the _streammessage, and write to the HTTP Response using the syntax expected by the HTML5 EventSource specification which is: “data:”, then serialized JSON, and then a new line (without the new line, the streaming of data will not work). To do that, we go back to the indispensable help of JSON.NET (by now, it’s already part of ASP.NET Web API core!). Since we iterate through that collection, the message gets written (pushed) to every client that has subscribed to the ChatController using the GET method. In other words, this is they key mechanism that will enable us to create this multi-person browser-based chat.

Client side code

Believe it or not, that’s everything we need on the server side. Let’s move on to the client side. First some HTML to have somethign to work with.

In the Index.chtml, first we need to add knockout.js references. These are easiest obtained via NuGet and then referenced like this:

Then we add our HTML.

A few words of explanation. We have here:
– a div with ID console. This will be our chat box. It is bound to a knockout.js template chatMessageTemplate. The template expects a JSON object with properties username, text (message body) and dt (date time). Additionally, after the template is applied we call a method resizeChat, to scroll the chat down.
– an input box for username, which is bound to a knockout.js viewModel property
– an input box for chat message, also bound to a knockout.js viewModel property
– a button to send chat Message
– the whole section of Message sending is hidden if no username is set. This is also achieved by utilizing knockout.js – its so called visible binding

viewModel

Our viewModel will look like this:

chatMessages will contain JSON objects representing incoming chat Messages from the server
chatUsername will contain current username
newMessage will contain the new Message as it’s being composed and prepared to be sent to the server. As soon as it’s sent, the contents af this are wiped out.
resizeChat is a method to be called after binding the chat template, as after every message is appended to the chat console, we need to scroll it down, so that we mimic a typical chat experience known from any chat application (latest messages at the bottom, chat box is always scrolled down to the bottom).

We apply the binidngs in the document.ready() event.

EventSource Javascript

The final piece of puzzle is using the HTML5 EventSource to listen to incoming data pushed out from the server. We’ll handle this in a second, but before, let’s just add the handler for message sending (upon clicking the “Send” button).

When the user click the button, we’ll send the message to the server (using PSOT event, as mentioned earlier). The message itself is already data bound with knockout.js, so we don’t need to harvest any data from DOM. All that’s needed is to use the knockout.js mapping plugin to unwrap the JSON object from the knockout.js object. Additionally, we clear the message box and message text from viewModel.

Now the EvenSource, which is going to act as our subscription mechanism. We’ll also add it into $(document).ready().

First we check for the support of EventSource. If that’s the case, we create a new EventSource object by passing the the URL to our ChatCaontroller. EventSource by default operates on GET request. This opens up a connection with text/event-stream” content type. Then all we need to do is add a few event listeners to the EventSource.

The key event of the EventSource is the onmessage event. When this is raised, we take the response from the server and parse it to JSON, and then push into viewModel.chatMessages which in turn does all the UI updates for us.

Notice the else clause there; I left that empty since I wanted to focus on native HTML5 implementation. However, there are ways of making this cross-browser. A good polyfill for that has been written by Yaffle and can be found here. You could use that as a fallback mechanism for other browsers.

Trying it out

That’s it! Pretty simple for such a useful feature. Let’s run this using three browser instances – and there is not much to explain, you can chat between the windows :-) The more clients join in, the easier it is to see how nicely our chat messages get pushed out to all of them.

Chrome:

Firefox

And again Chrome, which joined the chat later, so doesn’t see the earlier messages:

Summary & source code

We built here a browser-based push-enabled chat, but the mechanism shown here could just as well be used for anything that requires push notifications, for example uploading services, sites using Twitter-like message system, external API querying and many many more.

As always, the source code is available. Normally I linked to a downloadable zip, but I’ve been requested to put stuff on github instead so that’s what I’m going to do. This time it’s a VS10 project. Enjoy!
source code on github

Be Sociable, Share!

  • Pingback: Dew Drop – May 7, 2012 (#1,321) | Alvin Ashcraft's Morning Dew

  • http://thewayofcode.wordpress.com/ Valerio

    Great article and very clear and detailed explanation!
    You really inspired me to try it out :)

    Valerio

    • Filip W

      Thanks a lot for your kind comment!

  • Christian Weyer

    Hey Filip – any reason why you did not chose SignalR for this? Or was t just an excercise for you? ;)

    • Filip W

      haha Christian, you are completely right. You could (and, I guess, should) do all that with SignalR, especially as it is soon joining the OSS ASP.NET Stack.

      With that said:
      – I hope the post shows how super extendible ASP.NET Web API is, and how easily you can build a rather complex functionality with it
      – PushStreamContent in ASP.NET Web API has it’s justified use cases so it is good to be familiar with it
      – and finally, as far as I’m aware, SignalR currently doesn’t support ASP.NET Web API selfhost (you’d have to run SignalR selfhost as a parallel process on different port) – and the code showed here would run fine on selfhost

      Plus, as you mentioned, there is nothing like building things yourself :)

      • Christian Weyer

        OK. SignalR will have a Web API hosting layer soon. Then things are way easier.

  • Pingback: Cool DevTools & Websites Issue #1 | Egypt Web Design

  • Shejoy

    Nice post Man :) I am trying it out already :)

  • Domlia

    This is just great article, thanks for posting!

  • Saeed

    OH, perfect. Thank you

  • twoflower

    Thanks for the great article. Anyway, am I missing something or PushStreamContent is no longer available in Web API RC?

    • Filip W

      Hi

      PushStreamContent was committed on April 23 and the cut off for Web API RC was April 21 (even though it was only released few days ago). To use PushStreamContent and all other post April 21 features you need to use the nightly builds or source from Codeplex.

      cheers
      /f

  • ניפוח זכוכית

    I could not resist commenting. Perfectly written!

  • Joe

    Hi there, this is clearly fabulous and well done for pulling it all together.

    I like that for moment this is nuts and bolts rather than leaning on a stack like SignalR but it does raise a question with me RE scalability: if I understand correctly in this setup when a client connects to the controller with Get, the connection to the client is held open by the server using the PushStreamContent class and queued for later event streaming. If this is what’s happening here the concern I have is how well the .NET web api will scale when connections are held open like this. Any insights here?

    • Filip W

      Hi Joe

      Thanks a lot for your nice comment.

      PushStreamContent is enitrely asynchronous, so it will not be blocking threads. Moreover, you can write data to it, in an async way as well (I didn’t do this in this demo, but you could easily do that). As long as your infrastructure can handle X amount of HTTP connections (which will be in idle state, as soon as the individual chunk is sent via HTTP) all of this should scale well. Important note – you should be careful of two things: 1) implement some mechanism that closes the EventSource request. You can do that through tracking eventIDs of connected clients and trigger the closing from the client or server whenever the client should no longer receive updates. 2) beware of not opening multiple unnecessary EventSources from one client. This can easily drain your connection pool; rule of thumb – one EventSource (identifiable by ID) should be enough to handle one client.

      With that said, as I wrote above, PushStreamContent is a totally new thing (didn’t even make it to the RC) so I don’t think anyone is using it yet for production scenarios so perhaps life would verify all that :)

      Finally, even though I used here chat as an example (two-way communication), PushStreamContent and SSE is even better suited for one-way communication i.e. checking new mail/messages, server sent notifications, news tickers, news feeds, pushing out updates for the client etc.

      cheers
      /f

      • Joe

        Thanks I agree that SSEs are very well suited / intended for one-way comms from the server but I do like your chat example too. I think I’ll run some tests on this to see how far it can be pushed on a single server.

  • Joe

    Hi Filip,

    For the moment I’ve ground to a halt with this. As I mention above I intend to benchmark this functionality for scale but I’ve encountered issues in my proof of concept app relating to client stream / connection management and I’ve not been able to resolve these yet. I’ve asked the question here http://aspnetwebstack.codeplex.com/discussions/359056 and there’s no response yet.

    In my tests I see that a client can connect to and then disconnect from the controller but the controller never knows or appears to have any way to detect the client has gone. This means that over time I see the number of connected clients in my ConcurrentQueue grow even when it has only been a single client connecting and disconnecting.

    It’s easy enough to implement a method to enable a client to perform a graceful disconnect from the controller however the problem I’m trying to mitigate is abnormal disconnection of a client (e.g. power failure). This means I’m really seeking a server managed approach for connection management. Perhaps I’m assuming that when a client disappears, the stream will change to a read-only state thereby providing the flag to remove the connection.

    In your opinion do you agree what I expect from the framework is reasonable or is it expected that a system like this would implement an “ImAlive” type method that the client would call on a long interval basis? (although this would work I must admit I don’t really like it because it seems to contradict what I’m trying to achieve here – a pure SSE mechanism with no polling).

    I look forward to receiving a response from the Web API team to the question I’ve posted but in the meantime I’d really appreciate any ideas you might have in this area.

    Thanks for any help

    • Filip W

      Hi

      Indeed, since HTTP is stateless, there is no easy way to detect when the client has been disconnected (as it is in SignalR or node.js, using sockets). From SSE perspective, the primary connection control lays on the client, and it is the client which should subscribe/unsubscribe. Of course, as you correctly point out, it still leaves unsolved all the situations when the connection has been abruptly terminated.

      The easiest way I can think of, to allow the server to keep track of the clients, is to leverage on the fact that the browsers automatically reconnect to SSE source, if the stream closes. So you could close the event-stream for a given subscriber after a message has been flushed to it (or at some regular intervals, i.e. 10 minutes) and remove the subscriber from the static subscribers pool then. Then the browser would automatically reconnect (by default after after 3 seconds, but it could be changed), so the subscriber would jump back into the pool. If the client is dead, obviously it won’t reconnect, and the mechanism of preventing memory leak (pool growing infinitely) should work.

      Of course, there is plenty of stuff to still work out here, like how to make sure the subscribers – forced into this reconnection extravaganza – wouldn’t miss any events and so on. This should be doable, by tracking last event id. More on that in the excellent article here – http://www.html5rocks.com/en/tutorials/eventsource/basics/. It also shows how control the automatic reconnection interval for the clients using the HTTP response.

      Hope this puts you back on the right track,
      Filip

      • Song

        Can you give code example for this one? It seems that removing an specific item from ConcurrentQueue is not easy.
        ” So you could close the event-stream for a given subscriber after a message has been flushed to it (or at some regular intervals, i.e. 10 minutes) and remove the subscriber from the static subscribers pool then.”

  • Joe

    Yes this makes sense and it does help a lot thank you.

    Henrik has also responded by saying that the Web API isn’t currently notified by the underlying framework (ASP.NET / WCF) about a socket disconnect from the client so there’s currently no way to determine whether a client is present or not except by attempting to write to the stream and catching the exception when it fails. This advice implies that it is theoretically possible for a socket disconnect to be detected in the Web API however it’s just not possible with the framework as it stands. Personally I consider this to be a bug / missing functionality and unfortunately it means that at the moment my ideal expectations of the framework aren’t fulfilled in this case.

    So far as trapping the exception on stream write, I’ve not seen an exception in this scenario but Henrik is convinced this does work and it’s in his sample code so I’ll have try to again on this one.

    The good news is that between your suggestions and Henriks’ advice I definitely do have a working solution – thanks again!

  • http://www.dotnetjalps.com Jalpesh Vadgama

    Nice job done!!

  • http://www.trgtechnologies.com Rakesh Kumar

    Nice one!!!

  • Pavan Kumar Josyula

    I like your article very much. Using SignalR these kind of applications can be done with much ease.

  • Eulah Canzoneri

    You amazingly come with wonderful article content. Cheers for revealing your web page.

  • vancouver

    I really liked your article.Thanks Again. Really Cool.

  • Pingback: BitterCoder – Long running tasks

  • Suresh Kumar

    Hi,
    i have a problem while load Get HttpRequestMessage on chat controller we face an issue like 500 internal server. i couldnt dig further.can you help me with this?

    • Filip W

      hi, well 500 is as generic as it could be :-) it basically says “exception”.

      Try adding this in the Global.asax GlobalConfiguration.Configuration.IncludeErrorDetailPolicy
      = IncludeErrorDetailPolicy.Always;

      and see if the response body contains any descriptive error (along the 500 status code)

  • rad

    Indeed nice article. On question: did you try it with the last bits of WEB Api? Either I’m missing something or something was changed. The messages are sent, but unfortunately nothing comes back. No errors, no exception…

  • Pingback: Long running tasks | Catch Blogs

  • http://thoughtresults.com Saeed Neamati

    Very nice. I wish to create everything myself over HTTP even in a text-based manner to understand more about what goes on behind the scenes. Do you have any article on that?

  • atul

    hi i working on a project. this is usefull 4 me so thanx

  • http://twitter.com/gencaslan Özgür Gencaslan

    works fine on visual studio 2010 built-in testing-server , but not when it is deployed on a remote windows-server.

    • http://twitter.com/gencaslan Özgür Gencaslan

      if you can’t deploy the project either do the following things.

      1. add knockout.mapping-latest.js to the project (the file is there, it is just not added).

      2. replace localhost:49999 to the application-address

      then it worked.

  • http://twitter.com/korzeniow Łukasz Korzeniowski

    Hi Filip W

    PushStreamContent class can no longer be found in the System.Net.Http.dll (System.Net.Http.Formatting.dll ? ) liblary….

    Do you know what happened? Or are you aware of a replacement class for PushStreamContent?

    • http://www.strathweb.com/ Filip W

      It’s still there, see screenshot http://image.bayimg.com/6967c8d7b9326661cb4ab24168378d673a618de8.jpg

      You need to double check your references because even though this class is under System.Net.Http namespace directly, it’s part of the System.Net.Http.Formatting.dll (not System.Net.Http.dll)

      • http://twitter.com/korzeniow Łukasz Korzeniowski

        Thanks, I think I found the issue.

        When you are starting the project as a MVC 4 Web API then you have the PushStreamContent class in System.Net.Http library.

        But when you are starting te project as a regular MVC 4 application then you don’t have PushStreamContent in that namespace.

        Which is rather strange

        • http://www.strathweb.com/ Filip W

          Like I said, it’s part of the System.Net.Http.Formatting.dll (not System.Net.Http.dll) and that DLL is distributed with Web API

  • Pingback: List of ASP.NET Web API and HttpClient Samples - .NET Web Development and Tools Blog - Site Home - MSDN Blogs

  • Alexander Abramov

    Tried it today and could only get it to work by explicitly specifying UTF8 encoding for the stream:
    new StreamWriter(stream,Encoding.UTF8)
    Until I did that the browsers just did not raise onmessage events at all without any errors/warnings.

  • Sami Ovaska

    Hi Filip, really nice post! Have you tried to use PushStreamContent with media type ”
    multipart/mixed; boundary=–myboundary”? It seems multipart media type is not supported with PushStreamContent, any idea why?