Building Real-time Web Apps with ASP.NET WebAPI and WebSockets

WebSocket is a recent technology that provides two-way communication over a TCP connection. This allows us to create real-time web apps where servers can push data to clients. In this blog post, I’ll demonstrate how this can be done by building a simple chat app using ASP.NET WebAPI and ASP.NET’s new support for WebSockets in .NET 4.5.

Before we get started, there are a few requirements for using WebSockets. It must be supported by both the browser and the web server. The WebSocket protocol is currently supported in Chrome, Firefox, and Safari and will be supported in the upcoming Internet Explorer 10 release. On the server side of things, you will need Windows 8 (or Windows Server 2012) to support WebSockets. Now you may not always be able to guarantee that your client browser and your web server support WebSockets. If that’s your case, I highly recommend you take a look at SignalR. SignalR provides you with the abstraction of a real-time, persistent connection without having to worry about how the data is being sent back and forth between the browser and the server.

Now once you’ve met the requirements, you’ll need to enable support for WebSockets on IIS. You can do so by going through Control Panel > Programs > Turn Windows features on or off. You’ll then need to make sure the following boxes are checked:

  • Internet Information Services > World Wide Web Services > Application Development Features > ASP.NET 4.5
  • Internet Information Services > World Wide Web Services > Application Development Features > WebSocket Protocol
  • .NET Framework 4.5 Advanced Services > ASP.NET 4.5

Next, it’s time to write our app. Start by creating a new Empty ASP.NET MVC 4 App in Visual Studio 2012. Create a new HTML page called “chat.htm” with this in the body:

    1: <form id="chatform" action="">
    2:     <input id="inputbox" />
    3: </form>
    4: <div id="messages" />

The HTML here is just a simple chat field to enter messages and a <div> to display our broadcast messages at. Next, let’s implement our client-side WebSocket functionality:

    1: $(document).ready(function () {
    2:  
    3:     var username = prompt('Please enter a username:');
    4:  
    5:     var uri = 'ws://' + window.location.hostname + window.location.pathname.replace('chat.htm', 'api/Chat') + '?username=' + username;
    6:     websocket = new WebSocket(uri);
    7:  
    8:     websocket.onopen = function () {
    9:         $('#messages').prepend('<div>Connected.</div>');
   10:  
   11:         $('#chatform').submit(function (event) {
   12:             websocket.send($('#inputbox').val());
   13:             $('#inputbox').val('');
   14:             event.preventDefault();
   15:         });
   16:     };
   17:  
   18:     websocket.onerror = function (event) {
   19:         $('#messages').prepend('<div>ERROR</div>');
   20:     };
   21:  
   22:     websocket.onmessage = function (event) {
   23:         $('#messages').prepend('<div>' + event.data + '</div>');
   24:     };
   25: });

This JavaScript depends on the JQuery library. Note how we’re sending a username in the query string for our initial web socket, and note how we’re sending messages over the websocket whenever a message is submitted, and that we display messages received over the websocket as well. Now it’s time to implement our server-side WebSocket handler.

First, let’s make sure that the right route is configured for WebAPI to receive the WebSocket upgrade request. You’ll need to run this command in your Application_Start method:

    1: routes.MapHttpRoute(
    2:     name: "DefaultApi",
    3:     routeTemplate: "api/{controller}/{id}",
    4:     defaults: new { id = RouteParameter.Optional }
    5: );

If you’re using an empty MVC project template, this should already be there for you in RouteConfig.cs. Next, let’s implement our WebAPI controller:

    1: public class ChatController : ApiController
    2: {
    3:     public HttpResponseMessage Get(string username)
    4:     {
    5:         HttpContext.Current.AcceptWebSocketRequest(new ChatWebSocketHandler(username));
    6:         return Request.CreateResponse(HttpStatusCode.SwitchingProtocols);
    7:     }
    8:  
    9:     class ChatWebSocketHandler : WebSocketHandler
   10:     {
   11:         private static WebSocketCollection _chatClients = new WebSocketCollection();
   12:         private string _username;
   13:  
   14:         public ChatWebSocketHandler(string username)
   15:         {
   16:             _username = username;
   17:         }
   18:  
   19:         public override void OnOpen()
   20:         {
   21:             _chatClients.Add(this);
   22:         }
   23:  
   24:         public override void OnMessage(string message)
   25:         {
   26:             _chatClients.Broadcast(_username + ": " + message);
   27:         }
   28:     }
   29: }

Our controller has just one method, Get, which listens for WebSocket upgrade requests. Since the initial upgrade handshake for WebSockets looks just like an HTTP request/response, this allows us to go through the entire WebAPI pipeline just as if it were any other HTTP request/response. This means for example that message handlers will run, action filters get called, and model binding lets us bind the request to action parameters. In the example above, we’ve bound the username from the query string as an action parameter. The Get action then does two things. It first accepts the web socket request and sets up a WebSocketHandler to manage the connection. It then sends back a response with HTTP status code 101, notifying the client that is has in fact agreed to switch to the WebSocket protocol. The ChatWebSocketHandler then just takes care of managing a list of chat clients and broadcasting the message to all clients when it receives a message. So we’re able to have WebAPI handle the initial WebSocket upgrade request and create a web socket handler to manage the lifetime of the connection.

You’ll need the Microsoft.WebSockets NuGet package to be able to build the controller above. Note that because of limitations of the WebSocket feature, you’ll need to deploy the app to IIS or IIS Express. This won’t work in the Visual Studio Development Server. Finally, you’ll need this in your Web.config to get WebSockets working:

    1: <configuration>
    2:   <appSettings>
    3:     <add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" />
    4:   </appSettings>
    5: </configuration>

And there you go, you should be able to deploy your app to IIS and test your chat app. You can try opening multiple browser windows to see chat messages broadcast in real-time to all the chat clients.