At the lowest level, OWIN middleware has the following signature:
Func<IDictionary<string, object>, Task>
This is a function that is passed the OWIN environment dictionary and returns a Task
.
You’ll often see if aliased like so:
using AppFunc = Func<IDictionary<string, object>, Task>;
So middleware could also be represented as:
Func<AppFunc, Task>
When adding middleware components to the OWIN pipeline we need to provide an object with the following signature (implicitly or explicitly):
Func<AppFunc, AppFunc>
Each middleware component gets a reference to the next step in the OWIN pipeline. Since AppFunc
returns a Task
it means we can await the next step, effectively performing processing on the way in and the way out (request / response).
To wire up middleware we call IAppBuilder.Use(object middleware)
. The middleware
parameter can be a delegate, type or instance.
Let’s look at each of these methods:
1. Inline function
app.Use(new Func<AppFunc, AppFunc>(next => (async env =>
{
Console.WriteLine("Begin Request");
await next.Invoke(env);
Console.WriteLine("End Request");
})));
The above middleware writes a message to Console at the beginning of the request. It then awaits the next
step in the pipeline. Once this has completed it writes out another message.
When you write middleware there’s no rule that you have to execute the next step in the pipeline. However, you still must receive it as an input parameter. This is why you’ll sometimes see OWIN examples like this:
app.Use(new Func<AppFunc, AppFunc>(ignoreNext => (env =>
{
Console.WriteLine("The request ends with me!");
return Task.FromResult(0);
})));
### 2. Delegate
The above example could also be achieved by passing an existing method as a delegate:
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.Use(new Func<AppFunc, AppFunc>(next => env => Invoke(next, env)));
}
private async Task Invoke(AppFunc next, IDictionary<string, object> environment)
{
Console.WriteLine("Begin Request");
await next.Invoke(environment);
Console.WriteLine("End Request");
}
}
You may prefer this if you’ve got more logic inside your middleware but don’t want to create a separate middleware class.
3. Middleware Type (most common)
When passing a middleware type you must create a class that has the following signature:
public class MyMiddlewareClass
{
public MyMiddlewareClass(AppFunc next)
{
}
public async Task Invoke(IDictionary<string, object> environment)
{
}
}
This is then registered like so:
app.Use(typeof(MyMiddlewareClass));
You can also provide additional constructor arguments that will be passed to your middleware component during initialisation, for example:
app.Use(typeof(MyMiddlewareClass), new SomeDependency());
Will pass the SomeDependency
object to your middleware constructor:
public MyMiddlewareClass(AppFunc next, SomeDependency dependency)
{
}
So we can now achieve the same example middleware like so:
public class LoggingMiddleware
{
private AppFunc next;
public LoggingMiddleware(AppFunc next)
{
this.next = next;
}
public async Task Invoke(IDictionary<string, object> environment)
{
Console.WriteLine("Begin Request");
await next.Invoke(environment);
Console.WriteLine("End Request");
}
}
To register the middleware we just pass its type.
app.Use(typeof(LoggingMiddleware));
4. Middleware Instance
We can also pass a middleware instance to IAppBuilder
. This is useful if you want to construct your middleware in advance (perhaps using dependency injection):
var logger = new LoggingMiddleware();
app.Use(logger);
You may wonder how we access the next step in the pipeline if the middleware has already been created. The answer is to add an Initialize
method to your middleware like so:
public class LoggingMiddleware
{
private AppFunc next;
public void Initialize(AppFunc next)
{
this.next = next;
}
public async Task Invoke(IDictionary<string, object> environment)
{
Console.WriteLine("Begin Request");
await next.Invoke(environment);
Console.WriteLine("End Request");
}
}
This is called automatically when the IAppBuilder
is first built.
5. OwinMiddleware
The Microsoft.Owin library includes an OwinMiddleware
base class for middleware. It provides strongly typed access to the OWIN environment via IOwinContext
.
public class LoggingMiddleware : OwinMiddleware
{
public LoggerMiddleware(OwinMiddleware next)
: base(next)
{
}
public async override Task Invoke(IOwinContext context)
{
Console.WriteLine("Begin Request");
await Next.Invoke(context);
Console.WriteLine("End Request");
}
}
To register the middleware you need to pass the middleware type (you can’t pass an instance):
app.Use(typeof(LoggingMiddleware));
Instances of OwinMiddleware
are automatically converted to the required middleware signature by using special type converters that are registered in Microsoft’s implementation of IAppBuilder
.
So there you have it, 5 different ways to achieve the same thing.