Creating OWIN Middleware using Microsoft Katana

Scott Brady
Scott Brady
Katana

If you’ve newed up an ASP.NET project template you’re most probably using the OWIN pipeline and are familiar with middleware such as Use, Map, Run or UseCookieAuthentication. These are all extension methods for IAppBuilder, found in your OWIN Startup class, calling logic that will manipulate the current OWIN context or environment.

Using the OwinMiddleware abstract class found in Microsoft.Owin we can start to create our own OWIN Middleware components that we can then integrate with our existing pipeline or distribute as a package.

OwinMiddleware

The abstract OwinMiddleware class looks like this:

/// <summary>
/// An abstract base class for a standard middleware pattern.
/// </summary>
public abstract class OwinMiddleware {

/// <summary> /// Instantiates the middleware with an optional pointer to the next component. /// </summary> /// <param name="next"></param> protected OwinMiddleware(OwinMiddleware next) { Next = next; }
/// <summary> /// The optional next component. /// </summary> protected OwinMiddleware Next { get; set; }
/// <summary> /// Process an individual request. /// </summary> /// <param name="context"></param> /// <returns></returns> public abstract Task Invoke(IOwinContext context); }

Instead of using the usual IDictionary<string, object> environment variable, OwinMiddleware uses the strongly typed abstractions found in Microsoft.Owin.IOwinContext. This includes abstractions such as request and response objects, saving us from searching the OWIN environment by key and a whole lot of casting.

A good takeaway from the above is that the Next property is optional, meaning we can stop the pipeline ourselves by simply not invoking the next OwinMiddleware.

To implement OwinMiddleware we will need a constructor that calls the base constructor and an implementation of the Invoke method. Let's spin up an example.

Example OwinMiddleware Implementation

To start off, we’ll create a new empty web application. Do not include any authentication or core packages/folders (i.e. no MVC or Web API, empty will do).

Required Packages

Install-Package Microsoft.Owin.Host.SystemWeb

This will also install the necessary dependencies of Owin and Microsoft.Owin.

OwinMiddleware

Next we’ll implement OwinMiddleware to return the HTTP status code 418: I’m a Teapot.

public class TeapotMiddleware : OwinMiddleware {
    public TeapotMiddleware(OwinMiddleware next) : base(next) {
    }

public async override Task Invoke(IOwinContext context) { context.Response.StatusCode = 418; // await this.Next.Invoke(context); } }

As we know from looking at the base class, the Next property can be null and in our case it will be. If you wanted to call the next middleware in the pipeline however, you would use the above commented out code of this.Next.Invoke(context) in your own Invoke method.

Startup

Now all we need is our OWIN startup class. We have two options for registering our middleware with the pipeline:

public class Startup {
    public void Configuration(IAppBuilder app) {
        // app.Use(typeof(TeapotMiddleware));
        app.Use<TeapotMiddleware>();
    }
}

Now when you start you project you’ll get something like the following:

HTTP/1.1 418
Server: Microsoft-IIS/8.0
X-Powered-By: ASP.NET
Date: Sat, 29 Aug 2015
Content-Length: 0

Congratulations. It’s a teapot.

UseOwinMiddleware

We now have a working OWIN Middleware component, but that app.Use doesn’t look very professional, especially if we’re going to share it with others. We can change this by creating an extension method for our middleware.

internal static class TeapotMiddlewareHandler {
    public static IAppBuilder UseTeapotMiddleware(this IAppBuilder app) {
        app.Use<TeapotMiddleware>();
        return app;
    }
}

Now we can register our middleware in the pipeline like so:

app.UseTeapotMiddleware();

OwinMiddlewareOptions

If you want to pass in a class to your Owin Middleware you’ll need to expand on the constructor. For this example I’ll use the common use case of passing in some configuration options.

Options Class

public sealed class TeapotOptions {
    public string Biscuit { get; set; }
}

Updated Middleware

private readonly TeapotOptions options;
    public TeapotMiddleware(OwinMiddleware next, TeapotOptions options) : base(next) {
        this.options = options;
    }

public async override Task Invoke(IOwinContext context) { context.Response.Cookies.Append("Biscuit", this.options.Biscuit); context.Response.StatusCode = 418; }

Updated Handler

To set our options in the middleware constructor we pass them in as params object[] args:

public static IAppBuilder UseTeapotMiddleware(
  this IAppBuilder app, TeapotOptions options) {
    app.Use<TeapotMiddleware>(options);
    return app;
}

Updated Startup

Now we can set our options in our startup class:

app.UseTeapotMiddleware(new TeapotOptions { Biscuit = "Hobnob" });

And get a new response of:

HTTP/1.1 418
Server: Microsoft-IIS/8.0
Set-Cookie: Biscuit=Hobnob;
X-Powered-By: ASP.NET
Date: Sat, 29 Aug 2015
Content-Length: 0

Dependency Injection

I’ve used a relatively simple use case here but things are not as clean when you want to pass in a class with its own dependencies and we want to start using dependency injection. Since the constructor for OwinMiddleware requires OwinMiddleware next, we cannot simply register the middleware itself and allow our DI container to resolve its dependencies.

From what I can see, if you do need to add another dependency to your middleware you have two options: new it up with a concrete implementation (bad) or pass it in using your project’s Dependency Resolver (anti-pattern, also different between MVC and Web API). If you find any nicer methods please tell me, as I’d love to know and update this section. This has apparently been resolved for ASP.NET 5, but that doesn’t help us now…

Writing to the Response Body

Whilst I won’t go into the full detail of how to manage the reading and writing of a response, I will give you a basic example and share a huge gotcha that caught me out. After banging my head against this for days, I ended up finding this line in a self-published book on Amazon by the fantastic Badrinarayanan Lakshmiraghavan:

In [the] case of streaming hosts, as soon as a middleware writes to the response stream, the client gets the response along with the response headers - Badrinarayanan Lakshmiraghavan, OWIN and Microsoft Katana 101

This means that once you start writing to the response body, you can no longer write to the response headers.

If you try modifying the response headers after writing to the response body in a separate middleware component, you will be met with a lovely Yellow Screen of Death with the message: 'Cannot register for 'OnSendingHeaders' event after response headers have been sent.'.

However, if you try doing this within the same middleware component, no exceptions or warnings are thrown or given; any new response headers will just not be assigned.

Let's expand on my previous example by adding a couple more things to the pipeline.

Logging In and Sending a File

First we will login a user using the IAuthenticationManager found in the OwinContext.

This also requires the CookieAuthentication middleware found in Microsoft.Owin.Security.Cookies.

To send back a file we’ll need to use the SendFileAsync method found in Microsoft.Owin.StaticFiles. This will also pull in the dependency Microsoft.Owin.FileSystems which can be used to display folder directories using OWIN.

Middleware Invoke Method

public async override Task Invoke(IOwinContext context) {
    // user login
    var claims = new List<Claim>();
    claims.Add(new Claim(ClaimTypes.Name, "Scott"));
    claims.Add(new Claim(ClaimTypes.Email, "[email protected]"));

var id = new ClaimsIdentity(claims, "cookie"); context.Authentication.SignIn(id);
// send file context.Response.ContentType = "image/jpeg"; await context.Response.SendFileAsync("/img/teapot.jpg"); }

Startup

app.UseCookieAuthentication(new CookieAuthenticationOptions { 
    AuthenticationType = "cookie"
});

app.UseTeapotMiddleware(new TeapotOptions { Biscuit = "Hobnob" });
app.UseOrderMiddleware();

If we run our middleware in this order we receive expected results:

OWIN Middleware Teapot

with the headers:

HTTP/1.1 418
Cache-Control: no-cache
Pragma: no-cache
Content-Type: image/jpeg
Expires: -1
Server: Microsoft-IIS/8.0
Set-Cookie: Biscuit=Hobnob; path=/
Set-Cookie: .AspNet.cookie=9ZjAu2tTrcSWPIpVcdkl0bOw68geFrO6GFJJMwjHHiOZi-4Uhdwt5wGHBesYUFE4mYmJ6rA2sgRCvmwSij5Y3gRlv6_TEVLiGzrUCDsR9AAi-zDwIFlIYtwLkYzi30rODqz3ISS-x6NsudDrSOot-gy12hdmJibbFvGsPcRpg4vI4EZjX7VobUCUaSEQMPZc-9S4jQIRnRgKuctRrKTEhl2IH9V56EA329Ga2_S7gtPWrJtLSVvbqBx557cx3r7BPMg978EpGA5sTctzgqfUYuUGFcPqxoea8_7JSzNyzteSrgBagKSt9c8TL4au4Xbr; path=/; HttpOnly
X-Powered-By: ASP.NET
Date: Sun, 30 Aug 2015
Content-Length: 32067

Now lets break the rules and switch our login and file send logic around. Now we still get our teapot but the headers have changed significantly:

HTTP/1.1 418
Content-Type: image/jpeg
Server: Microsoft-IIS/8.0
Set-Cookie: Biscuit=Hobnob; path=/
X-Powered-By: ASP.NET
Date: Sun, 30 Aug 2015
Content-Length: 32067

No authenticated users and no exceptions.

OWIN and Microsoft Katana 101

If you want to take this further, I have to recommend OWIN and Microsoft Katana 101 by Badrinarayanan Lakshmiraghavan. Whilst it’s a little out of date now, you’ll find a lot of sanity saving snippets like the quote earlier that are only ever stated in this book.

Microsoft.Owin Dependency

It is worth pointing out that I have taken a dependency on Microsoft.Owin in these examples by using the OwinMiddleware abstract class within my pipeline. To remove this dependency you’ll need to instead use Func<IDictionary<string, object>> instead of OwinMiddleware in your constructor and IDictionary<string, object> env in your Invoke method.

You can still use the IOwinContext abstractions within your Invoke method however, as this would not make the pipeline itself dependent on Micorsoft.Owin, only the logic in your middleware. You can convert your env parameter like so:

IOwinContext context = new OwinContext(env);

GitHub

I’ve uploaded a working solution using the above code to GitHub. You can download this and easily run it to jump in and start messing with Owin middleware.