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.)
Create an empty web application project and name it owin2.
From the Package Manager Console (PMC), run the following command:
Install-Package Microsoft.Owin.Host.SystemWeb
Add an
OWIN Startup Class
and name itStartup
. 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); } } }
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,
}
By default, OMCs run at the last event (
PreHandlerExecute
). That's why our first example code displayed "PreExecuteRequestHandler".You can use the a
app.UseStageMarker
method to register a OMC to run earlier, at any stage of the OWIN pipeline listed in thePipelineStage
enum.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 toapp.UseStageMarker
. For example, after calling:app.UseStageMarker(PipelineStage.Authorize);
calls to
app.UseStageMarker
passingAuthenticate
orPostAuthenticate
will not be honored, and no exception will be thrown. OMCs run at the latest stage, which by default isPreHandlerExecute
. 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.The earliest stage of calls to
app.UseStageMarker
wins. For example, if you switch the order ofapp.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 theAuthenticate
event, and theAuthenticate
event precedes all other events.