OdeToCode IC Logo

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.