Writing an Orchard Owin middleware

So you heard about how fancy Owin is, with all of its loosely-coupled thingies? Well, it's now in Orchard: as you may have heard on this week's Community Meeting, you can now write Owin Middlewares in the Orchard-y way. Let's see how!

We won't discuss how Owin works or what a middleware is, so if you don't know these yet, check out the linked meeting video. Also, make sure to grab the latest source from the 1.x branch of Orchard's repository because only the upcoming 1.9 version will have Owin support.

First you'll need to get familiar with the IOwinMiddlewareProvider interface. Middleware providers are injected services that return a collection of OwinMiddlewareRegistration objects. The latter ones contain the actual middlewares, i.e. those delegates that will run when the Owin pipeline is executed. This all is where the magic happens: you need to implement IOwinMiddlewareProvider and inside your implementation create OwinMiddlewareRegistration instances. See the following example:

public class OwinMiddleware : IOwinMiddlewareProvider
{
// Mostly you'll only need the WCA, see below why.
private readonly IWorkContextAccessor _wca;
// Or use Work<T> injections, also see below for the explanation.
private readonly Work<ISiteService> _siteServiceWork;
public ILogger Logger { get; set; }
public OwinMiddleware(
IWorkContextAccessor wca,
Work<ISiteService> siteServiceWork)
{
_wca = wca;
_siteServiceWork = siteServiceWork;
Logger = NullLogger.Instance;
}
public IEnumerable<OwinMiddlewareRegistration> GetOwinMiddlewares()
{
return new[]
{
// Although we only construct a single OwinMiddlewareRegistration here, you could return multiple ones of course.
new OwinMiddlewareRegistration
{
// The priority value decides the order of OwinMiddlewareRegistrations. I.e. "0" will run before "10", but registrations
// without a priority value will run before the ones that have it set.
// Note that this priority notation is the same as the one for shape placement (so you can e.g. use ":before").
Priority = "50",
// This is the delegate that sets up middlewares.
Configure = app =>
// This delegate is the actual middleware.
// Make sure to add using Owin; otherwise you won't get why the following line won't compile.
// The context is the Owin context, something similar to HttpContext; the next delegate is the next middleware
// in the pipeline.
// Note that you could write multiple configuration steps here, not just this one.
app.Use(async (context, next) =>
{
// Note that although your IOwinMiddlewareProvider behaves like an ordinay Orchard dependency, the middleware
// delegate lives on its own and will run detached from the provider! Because of this you'll need to either
// access the Work Context as we do here, or inject your dependencies as Work<TDependency> objects. If you
// build multiple middlewares with many dependencies here, doing the following is a better choice.
var workContext = _wca.GetContext();
// But this would be an alternative:
var siteSettings = _siteServiceWork.Value.GetSiteSettings();
var clock = workContext.Resolve<IClock>();
var requestStart = clock.UtcNow;
// We let the next middleware run, but this is not mandatory: if this middleware would return a cached page
// for example then we could just leave this out.
await next.Invoke();
// Think twice when wrapping this call into a try-catch: here you'd catch all exceptions that would normally
// result in a 404 or an 503, so it's maybe better to always let them bubble up. But keep in mind that any
// uncaught exception here in your code will result in a YSOD.
var requestDuration = clock.UtcNow - requestStart;
// No need to use the ugly HttpContext, because we have OwinContext!
var url = context.Request.Uri;
// OK, but what if we _really_ need something from HttpContext?
if (context.Environment.ContainsKey("System.Web.HttpContextBase")) // This is Orchard, so should be true...
{
var httpContext = context.Environment["System.Web.HttpContextBase"] as System.Web.HttpContextBase;
if (httpContext != null)
{
// Voilá, we have the ugly HttpContext again! Like RouteData:
var routeDataValues = httpContext.Request.RequestContext.RouteData.Values;
// ...you know what to do.
}
}
Logger.Information("The request to " + url + " on the site " + siteSettings.SiteName + " had taken " + requestDuration + "time.");
// You see, we've done something useful!
})
}
};
}
}

Oh, and you'll see inline documentation for all the Owin interfaces :-). Also this sample will be part of the Training Demo module.

Great question. I have no idea to be honest but I'd encourage you to try it out.

Now all the middlewares run before Orchard (Owin is now practically something that runs before everything else in Orchard, that was in previous versions handling the request) so I'd guess you can also hook up custom routes.

1) It's not too much of use right now (because Orchard is not just a simple MVC app you can't directly use the middlewares out there...) but good most of the time instead an HTTP module, e.g. see: http://orchardazureappinsights.codeplex.com/
2) Please search the code for "Owin" and you'll see: basically middlewares run now at the foundational level and Orchard itself is a middleware.

System.Web will be loaded (i.e. the assembly) any way because it's statically referenced and several types need it any way, but you can prevent other pieces of the pipeline (also what was in an Orchard request before) from executing.