SignalR, Filters and ServiceStack

Β· 765 words Β· 4 minutes to read

I recently blogged about a technique of integrating SignalR with ASP.NET Web API through the use of action filters.

A few people have asked, whether this approach (and SignalR now being part of Microsoft’s ASP.NET product group after all) works with other frameworks. And the answer is absolutely yes.

Let’s have a look at implementing this for ServiceStack.

Adding ServiceStack πŸ”—

You should probably have a look at the original article first, since the SignalR part of it will be identical. Let’s start today by adding ServiceStack through Nuget:

install-package servicestack  

To enable ServiceStack endpoints, we also need to add SS handler to web.config, under the handlers section:

<add path="service" name="ServiceStack.Factory" type="ServiceStack.WebHost.Endpoints.ServiceStackHttpHandlerFactory, ServiceStack" verb="*" preCondition="integratedMode" resourceType="Unspecified" allowPathInfo="true" />  

This will setup all requests to /service to go through the ServiceStack pipeline. Additionally, we need to exclude this route from MVC processing, by adding the following ignore directive:

routes.IgnoreRoute("service/{*pathInfo}");  

The SignalR setup (hubs, JavaScript, HTML, routes) is the same as in the previous post.

Creating ServiceStack filters πŸ”—

The only difference from the Web API approach is that with service stack you get separate filter for pre-execution of the service method, and a separate one for post-execution.

Therefore, instead of implementing one attribute, like before, we will do it in two. Just to remind you (if you really didn’t read the last post) - in the request we will capture the body and flush down in real time to the “admin” interface together with a timestamp. The same will happen when the response gets sent down to the client; all of which combines into a neat real time logging mechanism

Request:

public class IncomingHubAttribute : Attribute, IHasRequestFilter  
{  
public string Name { get; set; }  
public string Method { get; set; }

public void RequestFilter(IHttpRequest req, IHttpResponse res, object requestDto)  
{  
if (string.IsNullOrWhiteSpace(Name) || string.IsNullOrWhiteSpace(Method)) return;  
var hub = GlobalHost.ConnectionManager.GetHubContext(Name);

if (hub != null)  
{  
hub.Clients.All.Invoke(Method,  
new  
{  
Time = DateTime.Now.ToString("G"),  
Data = req.HttpMethod + " " + req.RawUrl + " " + req.GetRawBody()  
});  
}  
} 

public IHasRequestFilter Copy()  
{  
return this;  
}

public int Priority  
{  
get  
{  
return -1;  
}  
}  
}  

And response:

public class OutgoingHubAttribute : Attribute, IHasResponseFilter  
{  
public string Name { get; set; }  
public string Method { get; set; }

public void ResponseFilter(IHttpRequest req, IHttpResponse res, object responseDto)  
{  
if (string.IsNullOrWhiteSpace(Name) || string.IsNullOrWhiteSpace(Method)) return;  
var hub = GlobalHost.ConnectionManager.GetHubContext(Name);

if (hub != null)  
{  
hub.Clients.All.Invoke(Method, new { Time = DateTime.Now.ToString("G"), Data = responseDto });  
}  
}

public IHasResponseFilter Copy()  
{  
return this;  
}

public int Priority  
{  
get  
{  
return -1;  
}  
}  
}  

In both cases, the principles are almost identical as with Web API; the main difference is that we don’t need to tack on the async API to deal with reading the request body and so on, since ServiceStack will expose all that synchronously.

However, in ServiceStack the default request behavior is bufferless input stream, whereas in Web API it is buffered. Bufferless obviously means that once ServiceStack took ownership of the request and pulled out the DTO from it, the request stream is at the end and not available to be re-read (which is what we are trying to do in the request filter).

To remedy that, we cans witch ServiceStack’s mode of operation to buffered, by adding this to our AppHost:

public HelloAppHost()  
: base("Hello Web Services", typeof(HelloService).Assembly)  
{  
PreRequestFilters.Add((req,res) => req.UseBufferedStream = true);  
}  

Running it πŸ”—

Now suppose you have a simple ServiceStack host:

public class HelloAppHost : AppHostBase  
{  
public HelloAppHost()  
: base("Hello Web Services", typeof(HelloService).Assembly)  
{  
PreRequestFilters.Add((req,res) => req.UseBufferedStream = true);  
}

public override void Configure(Container container)  
{  
Routes  
.Add<Hello>("/hello")  
.Add<Hello>("/hello/{Name}");  
}  
}

public class Hello  
{  
public string Name { get; set; }  
}

[ServiceStack.ServiceHost.Route("/hello")]  
public class HelloResponse  
{  
public int Id { get; set; }  
public string Name { get; set; }  
}  

We could add our attributes to the service:

public class HelloService : Service  
{  
[IncomingHub(Name = "servicelog", Method = "log")]  
[OutgoingHub(Name = "servicelog", Method = "logMessage")]  
public object Get(Hello request)  
{  
return new HelloResponse { Id = 1, Name = "Hello world" };  
}

[IncomingHub(Name = "servicelog", Method = "log")]  
public void Post(Hello request)  
{  
}  
}  

And if we navigate to the same log.html which we created in the last post for Web API, and start making requests, the log will start filling in real time.

This is pretty neat and shows you an easy way to do SignalR integration. On a side note, ServiceStack already has a pretty robust logging infrastructure, so this might not be the best use case, but hopefully it illustrates the concept.

About


Hi! I'm Filip W., a software architect from ZΓΌrich πŸ‡¨πŸ‡­. I like Toronto Maple Leafs πŸ‡¨πŸ‡¦, Rancid and quantum computing. Oh, and I love the Lowlands 🏴󠁧󠁒󠁳󠁣󠁴󠁿.

You can find me on Github, on Mastodon and on Bluesky.

My Introduction to Quantum Computing with Q# and QDK book
Microsoft MVP