IIS 集成管道中的 OWIN 中间件

作者 :Praburaj ThiagarajanRick Anderson

本文介绍如何在 IIS 集成管道中 (OMC) 运行 OWIN 中间件组件,以及如何设置运行 OMC 的管道事件。 阅读本教程之前,应查看 Project KatanaOWIN 启动类检测 概述。 本教程由 Rick Anderson ( @RickAndMSFT ) 、Chris Ross、Praburaj Thiagarajan 和 Howard Dierking ( @howard_dierking ) 编写。

尽管 OWIN 中间件组件 (OMC) 主要设计为在与服务器无关的管道中运行,但可以在 IIS 集成管道中运行 OMC, (经典模式) 不支持。 可以通过从包管理器控制台 (PMC) 安装以下包,使 OMC 在 IIS 集成管道中正常工作:

Install-Package Microsoft.Owin.Host.SystemWeb

这意味着,所有应用程序框架(即使是尚不能在 IIS 和 System.Web 外部运行的应用程序框架)都可以从现有的 OWIN 中间件组件中受益。

注意

Microsoft.Owin.Security.* Visual Studio 2013 (中的新标识系统随附的所有包(例如:Cookie、Microsoft 帐户、Google、Facebook、Twitter、持有者令牌、OAuth、授权服务器、JWT、Azure Active directory 和 Active Directory 联合身份验证服务) 都创作为 OMC,并且可用于自承载和 IIS 托管方案。

OWIN 中间件如何在 IIS 集成管道中执行

对于 OWIN 控制台应用程序,使用 启动配置 生成的应用程序管道按使用 IAppBuilder.Use 方法添加组件的顺序进行设置。 也就是说, Katana 运行时中的 OWIN 管道按照使用 IAppBuilder.Use注册的次序处理 OMC。 在 IIS 集成管道中,请求管道由订阅了一组预定义的管道事件(例如 BeginRequestAuthenticateRequestAuthorizeRequest 等)的 HttpModules 组成。请注意,Microsoft.Owin.Host.SystemWeb nuget 包注册 。OwinHttpModule 通常, HttpModule 通过 Web.config 文件在 IIS 中注册,但 Microsoft.Owin.Host.SystemWeb 使用名为 PreApplicationStartMethodAttribute 的 IIS 功能,将 HttpApplication.RegisterModule(Type) 动态注册 OwinHttpModule 到 IIS 管道。

如果将 OMC 与 ASP.NET 世界中的 HttpModule 进行比较,则必须将 OMC 注册到正确的预定义管道事件。 例如,当请求到达管道中的 AuthenticateRequest 阶段时,将调用 HttpModuleMyModule

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.
    }
}

为了让 OMC 参与这种基于事件的相同执行排序, Katana 运行时代码会扫描 启动配置 ,并将每个中间件组件订阅到集成管道事件。 例如,通过以下 OMC 和注册代码,可以查看中间件组件的默认事件注册。 (有关创建 OWIN 启动类的更详细说明,请参阅 OWIN 启动类检测。)

  1. 创建一个空的 Web 应用程序项目并将其命名为 owin2

  2. 从包管理器控制台 (PMC) 运行以下命令:

    Install-Package Microsoft.Owin.Host.SystemWeb
    
  3. 添加 并将其 OWIN Startup Class 命名为 Startup。 将生成的代码替换为以下 () 突出显示更改:

    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. 按 F5 运行应用。

启动配置设置包含三个中间件组件的管道,前两个组件显示诊断信息,最后一个用于响应 (事件,还显示) 诊断信息。 方法 PrintCurrentIntegratedPipelineStage 显示对此中间件调用的集成管道事件和消息。 输出窗口显示以下内容:

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

默认情况下,Katana 运行时将每个 OWIN 中间件组件映射到 PreExecuteRequestHandler ,这对应于 IIS 管道事件 PreRequestHandlerExecute

阶段标记

可以使用 扩展方法将 OMC 标记为在管道 IAppBuilder UseStageMarker() 的特定阶段执行。 若要在特定阶段运行一组中间件组件,请在注册期间设置的最后一个组件之后插入阶段标记。 在管道的哪个阶段可以执行中间件,并且组件必须运行的顺序 (规则将在教程) 后面部分介绍。 UseStageMarker将 方法添加到代码中,Configuration如下所示:

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);
}

调用 app.UseStageMarker(PipelineStage.Authenticate) 配置以前注册的所有中间件组件 (在这种情况下,我们的两个诊断组件) 在管道的身份验证阶段运行。 显示诊断并响应请求的最后一个中间件组件 (,) 将在 ResolveRequestCache 事件) (阶段运行ResolveCache

按 F5 运行应用。输出窗口显示以下内容:

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

阶段标记规则

可以将 OMC) (Owin 中间件组件配置为在以下 OWIN 管道阶段事件中运行:

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. 默认情况下,OMC 在) (PreHandlerExecute 最后一个事件运行。 这就是我们的第一个示例代码显示“PreExecuteRequestHandler”的原因。

  2. 可以使用 app.UseStageMarker 方法注册 OMC,以便更早地在枚举中列出的 PipelineStage OWIN 管道的任何阶段运行。

  3. OWIN 管道和 IIS 管道是有序的,因此对 app.UseStageMarker 的调用必须按顺序进行。 不能将事件处理程序设置为在 向 注册 app.UseStageMarker的最后一个事件之前的事件。 例如, 在调用后

    app.UseStageMarker(PipelineStage.Authorize);
    

    不会遵循对app.UseStageMarker传递 或 PostAuthenticateAuthenticate调用,并且不会引发异常。 OMC 在最新阶段运行,默认情况下为 PreHandlerExecute。 阶段标记用于使它们提前运行。 如果按顺序指定阶段标记,我们将舍入到前面的标记。 换句话说,添加阶段标记显示“不晚于阶段 X 运行”。 OMC 在 OWIN 管道的后面添加的最早阶段的运行标记。

  4. 对获胜的调用 app.UseStageMarker 的最早阶段。 例如,如果切换上一示例中的 app.UseStageMarker 调用顺序:

    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);
    }
    

    输出窗口将显示:

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

    OMC 全部在 AuthenticateRequest 阶段中运行,因为最后一个 OMC 注册到 Authenticate 事件,并且事件 Authenticate 先于所有其他事件。