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.


Comments
gravatar Khalid Abuhakmeh Monday, November 11, 2013
The thing I still don't get is how to Order things with OWIN? If you are building into the OWIN pipeline, I would assume you want to create some ordering. I understand there are stage markers you can reference your middleware to hook into, but from reading it seems like calling the stage markers can override other middleware causing a headache. Notice how the last call to UseStageMarker overrides the previous one. So how do you keep them from overwriting each other?! Link: http://www.asp.net/aspnet/overview/owin-and-katana/owin-middleware-in-the-iis-integrated-pipeline OWIN is interesting, but I wonder if it is fully baked or if there will be some growing pains when it gets to production systems? All that said, I am excited about it.
gravatar Scott Monday, November 11, 2013
@Khalid The stage markers are specific to hosting in IIS, I'm not sure how I feel about that. In general when you add middleware to the IAppBuilder object you have to be aware (beware) of the order. It's a bit like routing and route tables where greedy routes are the last routes to register. You place the greedy middleware last (something like NancyFx or WebAPI) if you want to give other middleware a chance to execute first.
gravatar Brent Friday, November 15, 2013
Scott I've seen a request on pluralsight to create a katana/owin video. I hope you pick it up :-)
gravatar Owe Me Saturday, November 23, 2013
What is a good way to speed-test one OWIN pipeline methodology (eg going low level with funcs) compared to alternatives? I have a system that requires the utmost speed, every ms counts, and I would like to evaluate speed options. Thanks.
gravatar Scott Monday, November 25, 2013
I haven't done any benchmarks myself, but I suspect there won't be a big difference between the class and raw functions. Rick Strahl has a good set of benchmarks here: http://weblog.west-wind.com/posts/2013/Nov/23/Checking-out-the-Helios-IIS-Owin-Web-Server-Host
Comments are closed.

My Pluralsight Courses

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