OWIN Middleware in the IIS integrated pipeline

by Praburaj Thiagarajan, Rick Anderson

This article shows how to run OWIN middleware Components (OMCs) in the IIS integrated pipeline, and how to set the pipeline event an OMC runs on. You should review An Overview of Project Katana and OWIN Startup Class Detection before reading this tutorial. This tutorial was written by Rick Anderson ( @RickAndMSFT ), Chris Ross, Praburaj Thiagarajan, and Howard Dierking ( @howard_dierking ).

Although OWIN middleware components (OMCs) are primarily designed to run in a server-agnostic pipeline, it is possible to run an OMC in the IIS integrated pipeline as well (classic mode is not supported). An OMC can be made to work in the IIS integrated pipeline by installing the following package from the Package Manager Console (PMC):

Install-Package Microsoft.Owin.Host.SystemWeb

This means that all application frameworks, even those that are not yet able to run outside of IIS and System.Web, can benefit from existing OWIN middleware components.

Note

All of the Microsoft.Owin.Security.* packages shipping with the new Identity System in Visual Studio 2013 (for example: Cookies, Microsoft Account, Google, Facebook, Twitter, Bearer Token, OAuth, Authorization server, JWT, Azure Active directory, and Active directory federation services) are authored as OMCs, and can be used in both self-hosted and IIS-hosted scenarios.

How OWIN Middleware Executes in the IIS Integrated Pipeline

For OWIN console applications, the application pipeline built using the startup configuration is set by the order the components are added using the IAppBuilder.Use method. That is, the OWIN pipeline in the Katana runtime processes OMCs in the order they were registered using IAppBuilder.Use. In the IIS integrated pipeline the request pipeline consists of HttpModules subscribed to a pre-defined set of the pipeline events such as BeginRequest, AuthenticateRequest, AuthorizeRequest, etc. Notice that the Microsoft.Owin.Host.SystemWeb nuget package registers the OwinHttpModule. Typically, HttpModule is registered in IIS via Web.config file, but Microsoft.Owin.Host.SystemWeb use a IIS feature called PreApplicationStartMethodAttribute and HttpApplication.RegisterModule(Type) to dynamically register the OwinHttpModule to IIS pipeline.

If we compare an OMC to that of an HttpModule in the ASP.NET world, an OMC must be registered to the correct pre-defined pipeline event. For example, the HttpModule MyModule will get invoked when a request comes to the AuthenticateRequest stage in the pipeline:

public class MyModule : IHttpModule
{
    public void Dispose()
    {
        //clean-up code here.
    }
    public void Init(HttpApplication context)
    {
        // An example of how you can handle AuthenticateRequest events.
        context.AuthenticateRequest += ctx_AuthRequest;
    }
    void ctx_AuthRequest(object sender, EventArgs e)
    {
        // Handle event.
    }
}

In order for an OMC to participate in this same, event-based execution ordering, the Katana runtime code scans through the startup configuration and subscribes each of the middleware components to an integrated pipeline event. For example, the following OMC and registration code enables you to see the default event registration of middleware components. (For more detailed instructions on creating an OWIN startup class, see OWIN Startup Class Detection.)

  1. Create an empty web application project and name it owin2.

  2. From the Package Manager Console (PMC), run the following command:

    Install-Package Microsoft.Owin.Host.SystemWeb
    
  3. Add an OWIN Startup Class and name it Startup. Replace the generated code with the following (the changes are highlighted):

    using System;
    using System.Threading.Tasks;
    using Microsoft.Owin;
    using Owin;
    using System.Web;
    using System.IO;
    using Microsoft.Owin.Extensions;
    [assembly: OwinStartup(typeof(owin2.Startup))]
    namespace owin2
    {
        public class Startup
        {
            public void Configuration(IAppBuilder app)
            {
                app.Use((context, next) =>
                {
                    PrintCurrentIntegratedPipelineStage(context, "Middleware 1");
                    return next.Invoke();
                });
                app.Use((context, next) =>
                {
                    PrintCurrentIntegratedPipelineStage(context, "2nd MW");
                    return next.Invoke();
                }); 
                app.Run(context =>
                {
                    PrintCurrentIntegratedPipelineStage(context, "3rd MW");
                    return context.Response.WriteAsync("Hello world");
                });            
            }
            private void PrintCurrentIntegratedPipelineStage(IOwinContext context, string msg)
            {
                var currentIntegratedpipelineStage = HttpContext.Current.CurrentNotification;
                context.Get<TextWriter>("host.TraceOutput").WriteLine(
                    "Current IIS event: " + currentIntegratedpipelineStage
                    + " Msg: " + msg);
            }
        }
    }
    
  4. Hit F5 to run the app.

The Startup configuration sets up a pipeline with three middleware components, the first two displaying diagnostic information and the last one responding to events (and also displaying diagnostic information). The PrintCurrentIntegratedPipelineStage method displays the integrated pipeline event this middleware is invoked on and a message. The output windows displays the following:

Current IIS event: PreExecuteRequestHandler Msg: Middleware 1
Current IIS event: PreExecuteRequestHandler Msg: 2nd MW
Current IIS event: PreExecuteRequestHandler Msg: 3rd MW

The Katana runtime mapped each of the OWIN middleware components to PreExecuteRequestHandler by default, which corresponds to the IIS pipeline event PreRequestHandlerExecute.

Stage Markers

You can mark OMCs to execute at specific stages of the pipeline by using the IAppBuilder UseStageMarker() extension method. To run a set of middleware components during a particular stage, insert a stage marker right after the last component is the set during registration. There are rules on which stage of the pipeline you can execute middleware and the order components must run (The rules are explained later in the tutorial). Add the UseStageMarker method to the Configuration code as shown below:

public void Configuration(IAppBuilder app)
{
    app.Use((context, next) =>
    {
        PrintCurrentIntegratedPipelineStage(context, "Middleware 1");
        return next.Invoke();
    });
    app.Use((context, next) =>
    {
        PrintCurrentIntegratedPipelineStage(context, "2nd MW");
        return next.Invoke();
    });
    app.UseStageMarker(PipelineStage.Authenticate);
    app.Run(context =>
    {
        PrintCurrentIntegratedPipelineStage(context, "3rd MW");
        return context.Response.WriteAsync("Hello world");
    });
    app.UseStageMarker(PipelineStage.ResolveCache);
}

The app.UseStageMarker(PipelineStage.Authenticate) call configures all the previously registered middleware components (in this case, our two diagnostic components) to run on the authentication stage of the pipeline. The last middleware component (which displays diagnostics and responds to requests) will run on the ResolveCache stage (the ResolveRequestCache event).

Hit F5 to run the app.The output window shows the following:

Current IIS event: AuthenticateRequest Msg: Middleware 1
Current IIS event: AuthenticateRequest Msg: 2nd MW
Current IIS event: ResolveRequestCache Msg: 3rd MW

Stage Marker Rules

Owin middleware components (OMC) can be configured to run at the following OWIN pipeline stage events:

public enum PipelineStage
{
    Authenticate = 0,
    PostAuthenticate = 1,
    Authorize = 2,
    PostAuthorize = 3,
    ResolveCache = 4,
    PostResolveCache = 5,
    MapHandler = 6,
    PostMapHandler = 7,
    AcquireState = 8,
    PostAcquireState = 9,
    PreHandlerExecute = 10,
}
  1. By default, OMCs run at the last event (PreHandlerExecute). That's why our first example code displayed "PreExecuteRequestHandler".

  2. You can use the a app.UseStageMarker method to register a OMC to run earlier, at any stage of the OWIN pipeline listed in the PipelineStage enum.

  3. The OWIN pipeline and the IIS pipeline is ordered, therefore calls to app.UseStageMarker must be in order. You cannot set the event handler to an event that precedes the last event registered with to app.UseStageMarker. For example, after calling:

    app.UseStageMarker(PipelineStage.Authorize);
    

    calls to app.UseStageMarker passing Authenticate or PostAuthenticate will not be honored, and no exception will be thrown. OMCs run at the latest stage, which by default is PreHandlerExecute. The stage markers are used to make them to run earlier. If you specify stage markers out of order, we round to the earlier marker. In other words, adding a stage marker says "Run no later than stage X". OMC's run at the earliest stage marker added after them in the OWIN pipeline.

  4. The earliest stage of calls to app.UseStageMarker wins. For example, if you switch the order of app.UseStageMarker calls from our previous example:

    public void Configuration(IAppBuilder app)
    {
        app.Use((context, next) =>
        {
            PrintCurrentIntegratedPipelineStage(context, "Middleware 1");
            return next.Invoke();
        });
        app.Use((context, next) =>
        {
            PrintCurrentIntegratedPipelineStage(context, "2nd MW");
            return next.Invoke();
        });
        app.UseStageMarker(PipelineStage.ResolveCache);
        app.Run(context =>
        {
            PrintCurrentIntegratedPipelineStage(context, "3rd MW");
            return context.Response.WriteAsync("Hello world");
        });
        app.UseStageMarker(PipelineStage.Authenticate);
    }
    

    The output window will display:

    Current IIS event: AuthenticateRequest Msg: Middleware 1
    Current IIS event: AuthenticateRequest Msg: 2nd MW
    Current IIS event: AuthenticateRequest Msg: 3rd MW
    

    The OMCs all run in the AuthenticateRequest stage, because the last OMC registered with the Authenticate event, and the Authenticate event precedes all other events.