OdeToCode IC Logo

Writing OWIN Middleware

Monday, November 11, 2013

Months ago I kicked off a series a posts about working with Katana and OWIN.

 

In those posts I mostly used helper methods provided by Katana packages to add functionality to the OWIN pipeline using simple Funcs.

public class Startup
{
    public void Configuration(IAppBuilder app)
    {         
        app.UseHandlerAsync((request, response) =>
        {
            res.ContentType = "text/plain";
            return res.WriteAsync("Hello, World!");
        });
    }
}

The helper method here is UseHandlerAsync, which is an extension method from Owin.Extensions that allows me to register a delegate that takes an OwinRequest and an OwinResponse and returns a Task. Behind the scenes, UseHandlerAsync transforms the OWIN environment of IDictionary<string, object> for each request into parameters that are easier to use (OwinRequest and OwinResponse) then calls into my code.  The end result is a pipeline that responds to every request with “Hello!”.

Once More, But A Little Bit Lower

You can work with Katana at a lower level and for more complex middleware, you typically will need more than just a delegate.

The basic pattern followed by most middleware components is to first provide an options object that someone can use to configure the middleware and customize the behavior for a specific application.

For example, let’s imagine we still want middleware to write a greeting like “Hello, World!”, but provide options to change the text and also include a timestamp.

public class HelloWorldOptions
{
    public HelloWorldOptions()
    {
        IncludeTimestamp = true;
        Name = "World";
    }

    public bool IncludeTimestamp { get; set; }
    public string Name { get; set; }
}

Now we can write a full middleware component that uses these options and the basic OWIN environment to respond to requests.

public class HelloWorldComponent
{
    readonly AppFunc _next;
    readonly HelloWorldOptions _options;

    public HelloWorldComponent(AppFunc next, HelloWorldOptions options)
    {
        _next = next;
        _options = options;
    }

    public async Task Invoke(IDictionary<string, object> environment)
    {
        var response = environment["owin.ResponseBody"] as Stream;
        using (var writer = new StreamWriter(response))
        {
            if (_options.IncludeTimestamp)
            {
                await writer.WriteAsync(DateTime.Now.ToLongTimeString());
            }
            await writer.WriteAsync("Hello, " + _options.Name + "!");
        }
    }
}

The class needs to have a constructor that takes an AppFunc and an options object. The AppFunc represents the next middleware component in the pipeline, and we’ll see an example in a later post where we will use the next AppFunc, but here we are ignoring any other middleware and taking control of every response.

The class also needs an Invoke method that matches the AppFunc delegate (Func<IDictionary<string, object>, Task>).

Finally, most middleware will provide extension methods for IAppBuilder that make it easy to configure themselves into the pipeline.

public static class AppBuilderExtensions
{
    public static void UseHelloWorld(
        this IAppBuilder app, HelloWorldOptions options = null)
    {
        options = options ?? new HelloWorldOptions();
        app.Use<HelloWorldComponent>(options);
    }
}

Now spinning up a web service that will greet the earth looks like this:

public class Startup
{
    public void Configuration(IAppBuilder app)
    {
        app.UseHelloWorld(new HelloWorldOptions
        {
            IncludeTimestamp = false,
            Name = "Earth"
        });
    }
}

The above code represents the same functionality we had before, but with a lot more flexibility.

Coming up next: logging middleware.