SignalR, Filters and ServiceStack - StrathWeb

Strath

StrathWeb. A free flowing web tech monologue.

March 3rd, 2013

  • servicestack
  • signalr

SignalR, Filters and ServiceStack

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:

Shell
1
install-package servicestack

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

XHTML
1
2
<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:

1
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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
    public class IncomingHubAttribute : Attribute, IHasRequestFilter
    {
        public string Name { get; set; }
        public string Method { get; set; }</p>
 
    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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
    public class OutgoingHubAttribute : Attribute, IHasResponseFilter
    {
        public string Name { get; set; }
        public string Method { get; set; }</p>
 
    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.
gipoco.com is neither affiliated with the authors of this page nor responsible for its contents. This is a safe-cache copy of the original web site.