Writing an Orchard Owin middleware

Zoltán Lehóczky's avatar
Development, Owin

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.

Happy Owin'!

9 Comments

  • johnk said Reply

    Will it be possible to self host some odata end points from a module.
    for example something like this :


    // Setup routes
    var config = new HttpConfiguration();


    // SetupOdataRoutes
    var builder = new ODataConventionModelBuilder();
    var customers = builder.EntitySet<Customer>("Customer");

    config.MapODataServiceRoute(
    routeName: "odata",
    routePrefix: "odata",
    model: builder.GetEdmModel());

    app.UseWebApi(config);


    Will this work with orchards way of managing routes and with muti-tenants ?
    Thanks
    John

  • Zoltán Lehóczky said Reply

    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.

    Any way, if you try it out please come back and tell what happened.

    • johnk said Reply

      Tried it out and got it working well with orchard routes on a single tenant.
      Learned that you have to declare your new self hosting routes config like this :
      Configure = app =>
      {

      // Setup routes
      var config = new HttpConfiguration();

      var builder = new ODataConventionModelBuilder();


      config.MapODataServiceRoute(
      routeName: "odata",
      routePrefix: "odata",
      model: builder.GetEdmModel());

      app.UseWebApi(config);

      }



      Use app.Use(async (context, next) => ...... to run on every orchard request

      Am not so sure it will work for multi tenants.

      John.

  • behnam esmaili said Reply

    Hi and thanks for your usefull article.i have a question about OWIN in Orchard.

    1 - What is OWIN good for in orchard , what we can do now having owin which we were not able to do?
    i mean iis and system.web are still here in orchard , what changed with bringing owin to orchard?

    2 - What changes made to Orchard.Framework library to persuade framework to use owin pipeline.

  • Zoltán Lehóczky said Reply

    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.

    • behnam esmaili said Reply

      Oh cool thanks.Do this means by breaking the chain (not caling next.Invode()) from our middleware we drop the load of System.Web?

      • Zoltán Lehóczky said Reply

        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.

Add a Comment