Sorting out the confusion that is OWIN, Katana and IAppBuilder
Posted at 07:00 on 28 August 2014
I've been doing some more work on my MVC hobby project lately, and one thing I've been working on has been replacing the rather poorly thought out abstraction layer around the host process with OWIN.
If you've never come across OWIN before, it's the new standard way of decoupling .net-based applications and frameworks from the servers that run them, a bit like WSGI for Python or Rack for Ruby. It means that you can host your web application not only in IIS but also in a console application, or a Windows application, or even in Apache under Mono on a Linux server. The first version of the standard was finalised about two years ago.
The OWIN specification is elegantly simple. You just have to provide a delegate of type Func<IDictionary<string, object>, Task>
-- or in other words, something that looks like this:
public Task SomeAppFunc(IDictionary<string, object> environment);
where the environment dictionary provides a standard set of keys containing things such as the request and response headers, body, and so on. This delegate is called the AppFunc. The values are all BCL types, so you don't have to take dependencies on anything else. In fact, the OWIN specification explicitly says this:
OWIN is defined in terms of a delegate structure. There is no assembly called
OWIN.dll
or similar. Implementing either the host or application side the OWIN spec does not introduce a dependency to a project.
So putting all this together, a "Hello World" OWIN application would look something like this:
public Task HelloWorldAppFunc (IDictionary<string, object> environment) { var responseHeaders = environment["owin.ResponseHeaders"] as IDictionary<string, string[]>; var responseBody = environment["owin.ResponseBody"] as Stream; responseHeaders["Content-Type"] = new string[] { "text/plain" }; var writer = new StreamWriter(responseBody); writer.WriteLine("Hello world"); }
That's it. But now we need to find somewhere to host it -- and here, we run up against a problem.
The problem is that most of the OWIN "hello world" tutorials that you see on the web simply don't look like that. Take for instance the one you see on www.asp.net:
public void Configuration(IAppBuilder app) { // New code: app.Run(context => { context.Response.ContentType = "text/plain"; return context.Response.WriteAsync("Hello, world."); }); }
Just a minute ... what's this IAppBuilder
? And where in the OWIN specification are there classes with a Response
property, or a ContentType
property and a WriteAsync
method?
What you are looking at is not OWIN itself, but a set of libraries created by Microsoft called Katana. These libraries provide, among other things, some strongly typed wrappers around the AppFunc
defined in the OWIN specification, so in one sense they're useful in reducing boilerplate code.
The problem here is that Katana is built on an obsolete pre-release draft of the OWIN specification. The IAppBuilder
interface was originally described in initial drafts of the OWIN specification, but it has since been removed. IAppBuilder
is defined in a NuGet package called owin.dll, but the community voted to sunset this back in May, and it's now considered deprecated; new OWIN-related libraries should not use it. That's why it's so difficult to find any documentation on IAppBuilder
itself: a Google search for IAppBuilder.Use
merely leads to a couple of extension methods provided by Katana.
So...given our nice shiny AppFunc
, how do we host it?
In theory, we should be able to just pass it to the host process. Some OWIN hosts, such as Nowin, let you do just that, by passing it into the ServerBuilder.SetOwinApp
method. With Katana, it's a little bit more complicated.
The IAppBuilder
interface declares a method called Use
, whose method signature looks like this:
void Use(object middleware, params object[] args)
Intuitively, you'd expect to be able to just pass your IAppBuilder
method into the Use
method. Unfortunately, if you try this, it throws an exception. What you actually have to do is to pass a middleware delegate. OWIN middleware (and this isn't documented in the spec) is a delegate which takes one AppFunc
and returns another AppFunc
:
using AppFunc = Func<IDictionary<string, object>, Task>; using MiddlewareFunc = Func<AppFunc, AppFunc>;
Confused? So was I at first.
The AppFunc
that was passed in to the MiddlewareFunc
is simply the next step in the chain. So your AppFunc
should do what it has to do, then either call or ignore the AppFunc
which was passed in. For example, this middleware would just log the start and end of each invocation to the console:
app.Use(new Func<AppFunc, AppFunc>(next => async env => { Console.WriteLine("Starting request"); await next(env); Console.WriteLine("Ending request"); }));
If you are writing a self-contained application rather than middleware, your AppFunc
will be the last step in the pipeline, so you will want to ignore the "next" AppFunc
. You would therefore do this:
app.Use(new Func<AppFunc, AppFunc>(ignored => HelloWorldAppFunc));
There are other ways of registering OWIN apps or middleware with a Katana host, by passing a middleware type or instance with a specific signature, or by using one of Katana's strongly-typed wrappers, but none of these are defined in the OWIN specification, so I won't dwell on them here.
Fortunately, this is set to be clarified in ASP.NET vNext: there's been a lot of feedback from the community that IAppBuilder
shouldn't be the only way of creating an OWIN pipeline, and that the Katana wrapper classes, OwinMiddleware
, OwinRequest
and OwinResponse
, have been causing some confusion, so the means to host a raw OWIN application or middleware will become more transparent. In the meantime, I hope that this clears up some of the confusion.