Implementing WebSockets in ASP.NET 4.6 using WebSocketHandler

This time I want to share with you a short example of how to implement WebSockets in your ASP.NET 4.6 Web API / AngularJS Application by using WebSocketHandler from Microsoft.WebSockets nuget Package.

Installing a Microsoft.WebSockets Nuget package

First of all you need to install the Microsoft.WebSockets from Nuget using Package Manager Console by typing:

PM> Install-Package Microsoft.WebSockets

Note that this Package is almost one year old now and not actively developed (says official Nuget package description, current version 0.2.3.1) but it is the best implementation I found so far if you are using it with ASP.NET 4.6.

You can also find a .NET Framework implementation of WebSockets in System.Web.WebSockets Namespace but I find it little bit impractical and if you want to implement it to be stable and multiclient, you will actually end up implementing the WebSocketHandler from Microsoft.WebSockets package.

Defining your Message Object

Here is a simple model of a Message Object that we will be sending to Client:

public class SimpleMessage

{

[JsonProperty("id")]

public long Id { get; set; }

 

[JsonProperty("body")]

public dynamic Body { get; set; }

[JsonProperty("sessionId")]

public Guid SessionId { get; set; }

}

Implementing your WebSocketHandler

Now we need to implement our own WebSocketHandler. I called it for purpose of this Post "SimpleWebSocketHandler" and it looks like this:

public class SimpleWebSocketHandler : WebSocketHandler

{

private static WebSocketCollection _wsClients = new WebSocketCollection();

 

public override void OnOpen()

{

_wsClients.Add(this);

 

base.OnOpen();

}

 

public override void OnClose()

{

wsClients.Remove(this);

 

base.OnClose();

}

 

public void SendMessage(SimpleMessage message)

{

if (string.IsNullOrEmpty(message.SessionId))

{

SendBroadcastMessage(message);

}

else

{

SendMessage(message, message.SessionId);

}

}

 

public void SendMessage(SimpleMessage message, string sessionId)

{

var webSockets = _wsClients.Where(s =>

{

var httpCookie = s.WebSocketContext.Cookies["SessionId"];

return httpCookie != null && httpCookie.Value == sessionId;

});

 

foreach (var socket in webSockets)

{

socket.Send(JsonConvert.SerializeObject(message));

}

}

 

public void SendBroadcastMessage(SimpleMessage message)

{

_wsClients.Broadcast(JsonConvert.SerializeObject(message));

}

}

As you can see, I am also using a WebSocketCollection object for storing my opened WebSocket connections. That way I can simply send a Broadcast message to all WebSocket connections or filter the one that has my Session ID as a cookie (that will be place upon first web request in Startup.cs below) and send the message only to that client.

Changes in Application Startup

Now we have to make sure that we initialize our "SimpleWebSocketHandler" upon application start. If you are using an OWIN Middleware and Unity Container (and you should), your Startup.cs should look something like this:

Startup.cs

public partial class Startup

{

public void Configuration(IAppBuilder app)

{

    var container = new UnityContainer();

    container

        .RegisterType<SimpleWebSocketHandler>();

 

DependencyResolver.SetResolver(new UnityDependencyResolver(container));

// register dependency resolver for WebAPI

configuration.DependencyResolver = new Unity.WebApi.UnityDependencyResolver(container);    

 

    UnityConfig.Initialise(GlobalConfiguration.Configuration);

ConfigureAuth(app);

ConfigureWebSocketListeners();

 

app.Use(new Func<AppFunc, AppFunc>(next => (async env =>

{

if (context.Request.Cookies["SessionId"] == null)

context.Response.Cookies.Append("SessionId", Guid.NewGuid().ToString(), new CookieOptions() { HttpOnly = false });

            

            await next.Invoke(env);

})));

}

}

Startup.WebSockets.cs

public partial class Startup

{

public void ConfigureWebSocketListeners(SimpleWebSocketHandler _webSocket)

{

ListenForSystemMessages(_webSocket);

}

 

private void ListenForSystemMessages(SimpleWebSocketHandler webSocket)

{

// Here you can register any event that should send the messages to users.

// It can be for example ServiceBus.OnMessage or anything else.

// queueClient.OnMessage(receivedMessage =>

// {

//    var msg = JsonConvert.DeserializeObject<SimpleMessage>(receivedMessage.GetBody<string>());

//    webSocket.SendMessage(msg);

//

//    queueClient.Complete(receivedMessage.LockToken);

// });

}

}

Note that I am injecting my SimpleWebSocketHandler over Unity Container that we configured in step above.

Implementing your Web API Controller

So now what we need to do is the register one WebAPI route/method ("GetMessages") that will be used as our WebSocket connection endpoint and will be open for whole user session.

[RoutePrefix("api/messages")]

public class MessageController : ApiController

{

private readonly SimpleWebSocketHandler _webSocketHandler;

 

public FrontendSystemMessageController(SimpleWebSocketHandler webSocketHandler)

{

_webSocketHandler = webSocketHandler;

}

 

[HttpGet]

[Route("GetMessages")]

public async Task<HttpResponseMessage> Messages()

{

var currentContext = HttpContext.Current;

 

return await Task.Run(() =>

{

if (currentContext.IsWebSocketRequest || currentContext.IsWebSocketRequestUpgrading)

{

currentContext.AcceptWebSocketRequest(ProcessWebSocketRequest);

return Request.CreateResponse(HttpStatusCode.SwitchingProtocols);

}

 

return Request.CreateResponse(HttpStatusCode.BadRequest);

});

}

 

private async Task ProcessWebSocketRequest(AspNetWebSocketContext context)

{

var sessionCookie = context.Cookies["SessionId"];

if (sessionCookie != null)

{

await _webSocketHandler.ProcessWebSocketRequestAsync(context);

}

}

}

Note that I am injecting my SimpleWebSocketHandler over Unity Container that we configured in step above.

AngularJS "WebSocketPoller" Factory

For this purpose I wrote a simple AngularJS factory that creates a WebSocket connection upon the app start and raises the event on every message that has been received.

'use strict';

 

app.factory('webSocketPoller', function() {

 

var listener;

var errorHandler;

var address;

 

return function(options) {

 

var Service = {};

 

if (options && options.address) {

address = options.address;

}

 

if (options && options.listener) {

listener = options.listener;

}

 

if (options && options.errorHandler) {

errorHandler = options.errorHandler;

}

 

Service.init = function()

{

var ws = new WebSocket(address);

 

ws.onmessage = function(message) {

//Do whatever you need with

//SimpleMessage Object:

//JSON.parse(message.data);

};

 

ws.onerror = errorHandler;

}

 

return Service

}

});

Now you can find a right AngularJS Controller or use app.run() to add following code that initializes the Puller and attaches to OnMessage and OnError so you can handle your messages on UI:

 

var wsPoller = new webSocketPoller({

address: 'ws://localhost/api/messages/GetMessages',

listener: onNewSystemMessageFunction,

errorHandler: function (error) { console.error(error); }

})

 

wsPoller.init();


Posted Dec 06 2016, 12:17 PM by Armin Kalajdzija
Filed under: ,
developers.de is a .Net Community Blog powered by daenet GmbH.