Once you get your head around OWIN’s Func<yness>
you begin to see how powerful it can be.
In my last post I discussed the different ways you can add middleware to the OWIN pipeline; one of which was to pass an inline delegate/lambda expression.
As I continue to hack around with OWIN I often find that I want to execute logic at the beginning and/or end of request.
The extension methods below enable you to register before and/or after pipeline hooks; so rather than having to write a middleware class you can just pass a delegate. I’m not advocating that you do this for complex middleware but these are useful extensions if you’re learning OWIN and want to understand how the pipeline fits together.
The UseHooks
/UseHooksAsync
extensions allow you to share state between the before and after hooks without polluting the OWIN environment dictionary (effectively wrapping the two hooks in a closure).
Example 1 - Timing the OWIN pipeline
app.UseHooks(
before: env => Stopwatch.StartNew(),
after: (stopwatch, env) =>
{
stopwatch.Stop();
Console.WriteLine(
"Request completed in {0} milliseconds.",
stopwatch.ElapsedMilliseconds);
}
);
Example 2 - Introducing latency
app.UseHooksAsync(before: env => Task.Delay(2000));
Example 3 - Logging the end of the request
app.UseHooks(after: env => Console.WriteLine(
"Completed request to {0}", env["owin.RequestPath"]));
Both UseHooks
and UseHooksAsync
have overloads for optionally passing state and as the method names suggest, one executes the hooks synchronously, the other asynchronously.
using AppFunc = Func<IDictionary<string, object>, Task>;
public static class OwinPipelineHookExtensions
{
public static IAppBuilder UseHooks(
this IAppBuilder app,
Action<IDictionary<string, object>> before = null,
Action<IDictionary<string, object>> after = null)
{
return app.Use(new Func<AppFunc, AppFunc>(next => (async env =>
{
if (before != null)
{
before.Invoke(env);
}
await next.Invoke(env);
if (after != null)
{
after.Invoke(env);
}
})));
}
public static IAppBuilder UseHooks<TState>(
this IAppBuilder app,
Func<IDictionary<string, object>, TState> before = null,
Action<TState, IDictionary<string, object>> after = null,
TState defaultState = default(TState))
{
return app.Use(new Func<AppFunc, AppFunc>(next => (async env =>
{
TState state = defaultState;
if (before != null)
{
state = before.Invoke(env);
}
await next.Invoke(env);
if (after != null)
{
after.Invoke(state, env);
}
})));
}
public static IAppBuilder UseHooksAsync(
this IAppBuilder app,
Func<IDictionary<string, object>, Task> before = null,
Func<IDictionary<string, object>, Task> after = null)
{
return app.Use(new Func<AppFunc, AppFunc>(next => (async env =>
{
if (before != null)
{
await before.Invoke(env);
}
await next.Invoke(env);
if (after != null)
{
await after.Invoke(env);
}
})));
}
public static IAppBuilder UseHooksAsync<TState>(
this IAppBuilder app,
Func<IDictionary<string, object>, Task<TState>> before = null,
Func<TState, IDictionary<string, object>, Task> after = null,
TState defaultState = default(TState))
{
return app.Use(new Func<AppFunc, AppFunc>(next => (async env =>
{
TState state = defaultState;
if (before != null)
{
state = await before.Invoke(env);
}
await next.Invoke(env);
if (after != null)
{
await after.Invoke(state, env);
}
})));
}
}