Shimming and Wrapping OWIN Middleware (Katana Part 5)

Wednesday, November 13, 2013

Sometimes I’m not interested in what the OWIN pipeline is doing as a whole, but I’m trying to figure out what each piece of middleware is doing in the pipeline configured by someone else. This is useful, for example, when debugging which piece of middleware is grabbing a request and short-circuiting the pipeline by sending an immediate response.

What the following IAppBuilder can do is wrap a real IAppBuilder and add a middleware wrapper for every piece of middleware added to the real IAppBuilder.

It’s middleware-middleware mania!

public class WrappingAppBuilder<TWrapper> : IAppBuilder
{
    private readonly IAppBuilder _inner;
    private readonly object _wrappingOptions;

    public WrappingAppBuilder(IAppBuilder inner, object wrappingOptions)
    {
        _inner = inner;
        _wrappingOptions = wrappingOptions;
    }

    public IAppBuilder Use(object middleware, params object[] args)
    {
        _inner.Use(typeof (TWrapper), _wrappingOptions, GetDescription(middleware));
        return _inner.Use(middleware, args);
    }

    private string GetDescription(object middleware)
    {
        var type = middleware as Type ?? middleware.GetType();
        return type.Name;
    }

    public object Build(Type returnType)
    {
        return _inner.Build(returnType);
    }

    public IAppBuilder New()
    {
        return _inner.New();
    }

    public IDictionary<string, object> Properties
    {
        get
        {
            return _inner.Properties;
        }
    }
}

The wrapper is specified as a generic type parameter. The following wrapper will allow an external observer to inspect the environment dictionary before it goes into the next piece of middleware, and then again during the return.

using AppFunc = Func<IDictionary<string, object>, Task>;

public class WrappingLogger
{
    private readonly AppFunc _next;
    private readonly WrappingOptions _options;
    private readonly string _middlewareDescription;

    public WrappingLogger(
        AppFunc next, 
        WrappingOptions options, 
        string middlewareDescription)
    {
        _next = next;
        _options = options;
        _middlewareDescription = middlewareDescription;
    }

    public async Task Invoke(IDictionary<string, object> environment)
    {
        _options.BeforeNext(_middlewareDescription, environment);
        await _next(environment);
        _options.AfterNext(_middlewareDescription, environment);
    }
}

The above class is using the following options.

using LogFunc = Action<object, IDictionary<string, object>>;

public class WrappingOptions
{ 
    public LogFunc BeforeNext { get; set; }
    public LogFunc AfterNext { get; set; }
}

And configuration is as easy as:

public void Configuration(IAppBuilder app)
{
    var options = new WrappingOptions()
    {
        BeforeNext = (middleware, environment) =>
            Debug.WriteLine("Calling into: " + middleware),
        AfterNext = (middleware, environment) =>
            Debug.WriteLine("Coming back from: " + middleware),
    };

    app = new WrappingAppBuilder<WrappingLogger>(app, options);

    // ...
    // the rest of the middleware
}

The above options provide some very crude request logging, but with a little work you can watch for dictionary changes and add timing information.

Coming up soon: the new identity bits.

Previous posts in this series.


Comments
gravatar dgon Thursday, November 21, 2013
I guess I should have asked this question loooong ago. But it is never late to sound foolish... Can you explain in simple terms what is a "middleware" in this context? :blush:
gravatar Scott Friday, November 22, 2013
@dgon, Sure - middleware are the pieces of software that sit in the HTTP processing pipeline. You can have a piece of middleware that checks for authorization cookies, another piece that adds compression, another one for logging. A lot of the existing middleware provides authentication and authorization functionality. Each piece of middleware generally has a specific and well defined job. They are quite a lot like HTTP modules, if you've ever work with modules in ASP.NET.
gravatar ZeVS Friday, December 20, 2013
Another one question. For my project I'm using SignalR, WebApi and Nancy. When I enter some url that doesn't exsist, Nancy gives me a 404 page and it's ok, but when I'm returning a 404 response from webApi, I get nothing, only blank page with 404 response. How can I say WebApi to redirect this request to nancy to return proper page. Should I write WrappAppBuilder like in your example to find these type of responses? Or there is a better way?
gravatar ZeVS Friday, December 20, 2013
And when i enter url starting with api/ and somthing wrong WebApi returns me a Json error object
gravatar ZeVS Friday, December 20, 2013
Sorry for tripple comment :). When the url is something like /signalr/somethingWrong then server shows Protocol error: Unknown transport. How to handle this? I think I need another pluralsight video... Nancy is just to bring the views, where webApi is for Json. And everything in self-host console application.
gravatar Scott Friday, December 27, 2013
@ZeVS: I'd probably break these questions down one by one and ask on stackoverflow.com for a quicker response :) Hope you are having a good holiday season and sorry for the slow response!
Comments are closed.

My Pluralsight Courses

K.Scott Allen OdeToCode by K. Scott Allen
What JavaScript Developers Should Know About ECMAScript 2015
The Podcast!