ASP.NET 与 IIS 7 的集成

作者:Mike Volodarsky

介绍

自发布以来,ASP.NET 一直是在 Windows/IIS 平台上开发 Web 应用程序的首选平台。 ASP.NET 2.0 使 Web 应用程序的开发提升到一个新的水平,开发人员能以比以往任何时候都更快的速度编译功能更强大的应用程序。

IIS 7 及更高版本通过将 ASP.NET 运行时扩展性模型与核心服务器集成,使 ASP.NET 进一步发展。 这使开发人员能利用 ASP.NET 2.0 和 .NET Framework 的丰富功能(而不是使用能力更弱的 IIS C++ API)来全面扩展 IIS 服务器。 现有 ASP.NET 应用程序还可以通过对各种内容使用现有的 ASP.NET 功能(例如窗体身份验证、角色和输出缓存),立即从更紧密的集成中受益。

虽然 IIS 默认提供改进的 ASP.NET 集成,但也有选择:IIS 支持新旧两种 ASP.NET 集成模式,这两种模式可在同一个服务器上并行使用。

本文探讨 ASP.NET 集成模式引入的改进以及这两种模式的体系结构,并讨论如何为 ASP.NET 应用程序选择并配置集成模式。

IIS 7 及更高版本中的 ASP.NET 增强功能

在 IIS 中更好地集成 ASP.NET 可增强现有应用程序,也使新应用程序能通过以下方式利用 ASP.NET 功能:

  • ASP.NET 服务可用于所有类型的内容。 在过去,窗体身份验证、角色、URL 授权和输出缓存等 ASP.NET 功能只能用于 ASPX 页面等 ASP.NET 内容类型。 静态页面、ASP 页面和其他内容类型则无法从这些服务中受益。

在 IIS 中,统一向所有内容提供所有 ASP.NET 服务。 例如,可保护所有 Web 内容(包括图像和 ASP 页面),且在 ASP.NET 窗体身份验证、成员资格和登录控制中生成现有的 ASP.NET 2.0 访问控制解决方案。

  • 充分扩展 IIS 与 ASP.NET。 以前版本的 IIS 由于 ASP.NET 的运行时限制,频繁要求通过使用本机 ISAPI 筛选器或扩展的扩展性模式来开发服务器扩展性。

IIS 使 ASP.NET 模块能直接插入到服务器管道中,且运行时保真度与使用本机 (C++) 服务器 API 开发的模块相同。 ASP.NET 模块可以在请求处理管道中执行所有运行时阶段,并且可以以与本机模块相关的任何顺序执行这些 ASP.NET 模块。 也扩展了 ASP.NET API,从而允许对请求处理拥有更多控制(相比以往可能的控制而言)。

  • 统一的服务器运行时。 更紧密的 ASP.NET 集成也统一了 IIS 和 ASP.NET 之间的许多功能。

IIS 为 IIS 和 ASP.NET 模块和处理程序提供统一的配置。 已统一许多其他功能(包括自定义错误和跟踪),以实现更好的管理和一致的应用程序设计。

ASP.NET 集成体系结构

在 IIS 6.0 和以前的版本中,ASP.NET 是作为 IIS ISAPI 扩展来实现的。

在这些较早的版本中,IIS 处理对 ASP.NET 内容类型的请求,然后将该请求转发给托管 ASP.NET 请求管道和页面框架的 ASP.NET ISAPI DLL。 对非 ASP.NET 内容(例如 ASP 页面或静态文件)的请求由 IIS 或其他 ISAPI 扩展处理,对 ASP.NET 不可见。

本模型的主要限制是 ASP.NET 模块和自定义 ASP.NET 应用程序代码提供的服务不可用于非 ASP.NET 请求。 此外,ASP.NET 模块无法影响发生在 ASP.NET 执行路径前后的 IIS 请求处理的某些部分。

Diagram that shows I I S 6 and A S P dot NET pipelines.

图 1:IIS 6.0 & ASP.NET 管道

在 IIS 中,ASP.NET 请求处理管道直接覆盖 IIS 管道,实质上是在其上提供了一个包装器,而不是直接插入其中。

IIS 处理对任何内容类型的请求,其中本机 IIS 模块和 ASP.NET 模块在所有阶段提供请求处理。 这使 ASP.NET 模块提供的服务(例如窗体身份验证或输出缓存)能用于请求 ASP 页面、PHP 页面和静态文件等。

直接插入服务器管道的能力使 ASP.NET 模块能替换任何 IIS 功能或在任何 IIS 功能前后运行。 例如,这使自定义 ASP.NET 基本身份验证模块(编写为使用成员资格服务和 SQL Server 用户数据库)能替代仅适用于 Windows 帐户的内置 IIS 基本身份验证。

此外,扩展的 ASP.NET API 使用直接集成来启用更多请求处理任务。 例如,ASP.NET 模块可以在其他组件处理请求之前修改请求头,方式是在 ASP 应用程序执行前插入 Accept-Language 标头,这会强制将已本地化的内容根据用户偏好发送回客户端。

Diagram that shows I I S 7 and above integrated mode.

图 2:IIS 7 及更高版本的集成模式

由于运行时集成,IIS 和 ASP.NET 可以使用相同的配置来启用和排列服务器模块,以及配置处理程序映射。 其他统一的功能包括跟踪、自定义错误和输出缓存。

兼容性

最值得注意的是,该体系结构维持 ASP.NET 运行时体系结构和 API,这使现有 ASP.NET 应用程序和服务能在安装后运行。 只需做少量修改,现有 ASP.NET 应用程序和服务就能利用新 ASP.NET 功能。

同样,开发人员可以继续对熟悉的 ASP.NET API 编写新应用程序,同时利用运行时集成的优势。

IIS 继续为 ASP.NET 应用程序(这些应用程序有集成模式不符合的特殊兼容性要求)提供经典 ASP.NET 模式。 管理员可以为每个应用程序池选择所需的集成模式,这样,使用新的和经典的 ASP.NET 模式的应用程序能在同一个服务器上并行工作。

将 ASP.NET 应用程序迁移到 IIS 7 及更高版本的集成模式

在 IIS 上,ASP.NET 配置为默认在新集成模式下运行。 这样,应用程序就能在修改量最少的情况下利用集成模式增强功能。

由于配置统一,一些应用程序可能需要迁移才能在集成模式下正常运行。 默认情况下,服务器会提供迁移帮助。 它监测需要迁移的应用程序,返回一条请求迁移应用程序的错误消息。

以下配置导致该迁移错误:

  1. 应用程序 Web.config 文件定义 <httpModules> 配置。
    该应用程序加载新的 ASP.NET 模块或删除现有的 ASP.NET 模块。
    在集成模式中,指定了 ASP.NET 模块,其中本机模块位于统一的 <system.webServer>/<modules> 配置部分。
    必须将在 <system.web>/<httpModules> 配置部分指定的 ASP.NET 模块移动到新的配置部分,这样才能生效。 随后,必须将新 ASP.NET 模块直接添加到统一的 <modules> 部分。
  2. 应用程序 Web.config 文件定义 <httpHandlers> 配置。
    应用程序为某些内容类型使用自定义处理程序映射。
    在集成模式中,必须在 <system.webServer>/<handlers> 配置部分指定 ASP.NET 处理程序映射才能生效。 随后,必须将新 ASP.NET 处理程序映射直接添加到统一的 <handlers> 部分。
    此部分替换 ASP.NET <httpHandlers> 配置和 IIS 脚本映射配置,先前必须已配置这两种配置才能设置 ASP.NET 处理程序映射。
  3. 应用程序 Web.config 文件定义 <identity impersonate="true" /> 配置。
    应用程序模拟客户端凭据,这在 Intranet 应用程序中最为常见。 在集成模式下,客户端模拟在某些早期请求处理阶段不可用。 在大多数情况下,这都不是问题,你可以关闭此迁移错误。 否则,你必须将此应用程序配置为通过使用经典 ASP.NET 模式运行。

生成此迁移错误时,通常可迁移应用程序配置(推荐在上面的第 1 和第 2 种情况中使用),或移动应用程序以使用经典 ASP.NET 模式(在情况 3 中)。

迁移应用程序配置

IIS 迁移应用程序的方式是:使用 AppCmd.exe 命令行工具来执行迁移。 迁移错误消息包含在命令行窗口中执行的命令(必须使用管理员用户权限运行),以即时将应用程序迁移到集成模式。

迁移命令的基本格式如下:

%windir%\system32\inetsrv\APPCMD.EXE migrate config <Application Path>

其中 <Application Path> 是包含站点名称的应用程序虚拟路径,例如“Default Web Site/app1”。

迁移完成后,应用程序将在集成模式和经典模式两种模式下运行,且没有任何问题。

注意

如果在迁移后更改配置,服务器不会提示你再次迁移。 在初始迁移后,你必须确保配置在两种模式之间保持同步。 你可以再次通过使用 AppCmd.exe 命令行工具来手动迁移应用程序。

迁移回经典 ASP.NET 迁移模式

如果更想回到经典 ASP.NET 模式,可将应用程序移动到一个配置为在经典模式下运行的应用程序池。 其他应用程序继续在新的集成模式下于该经典模式应用程序一起并行运行。

有关如何将应用程序迁移到经典 ASP.NET 模式的详细信息,请参阅“更改应用程序的 ASP.NET 模式”部分。

禁用迁移错误消息

如果已手动迁移配置或者希望保持集成模式而不迁移配置(不推荐),你可以通过向应用程序的 Web.config 文件添加以下代码来禁用迁移错误消息:

<system.webServer> 
    <validation validateIntegratedModeConfiguration="false" />    
</system.webServer>

注意

服务器在使用 AppCmd.exe 命令行工具迁移配置后,自动禁用该错误消息。

如果手动禁用迁移错误消息,你必须确保应用程序在集成模式下正常运行,因为服务器不再提供任何有关不支持的配置的警告。

更改应用程序的 ASP.NET 模式

如果应用程序在集成 ASP.NET 模式下不能正常运行,你可以如果将它移动到另一个应用程序池来将它迁移到经典 ASP.NET 模式。 每个应用程序池单独配置为使用所需的 ASP.NET 集成模式。 这使你能运行多组使用不同 ASP.NET 集成模式在同一个服务器上并行运行的应用程序。

更改应用程序的 ASP.NET 集成模式:

  1. 查找或创建一个配置为使用所需模式的应用程序池。
    在默认情况下,所有新应用程序池都在集成模式下运行。
    ASP.NET 设置提供一个名为“经典 .NET AppPool”的应用程序池,该池在经典 ASP.NET 集成模式下运行。 对于不能在集成模式下运行的应用程序,可以使用此应用程序池。
    你还可以更改现有应用程序池的 ASP.NET 模式,方法是:使用 IIS 管理工具或 AppCmd.exe 命令行工具,或者手动编辑应用程序池配置。
  2. 将应用程序设置为使用该应用程序池。
    每个应用程序都配置为使用一个特定的应用程序池。 默认情况下,所有应用程序都使用名为“DefaultAppPool”的默认应用程序池(该池在集成模式下运行)。
    你可以更改应用程序的应用程序池,方法是:使用 IIS 管理工具或 AppCmd.exe 命令行工具,或者手动编辑应用程序配置。

为应用程序选择 ASP.NET 版本

过去,IIS 已支持多个 ASP.NET/CLR 版本并行。 例如,IIS 使相同的服务器能使用 .NET Framework v1.1 和 v2.0 运行 ASP.NET 应用程序。 提供这种支持的方式是:通过使用 IIS 脚本映射配置,映射 ASPNET_isapi.dll 的相应版本来提供 ASP.NET 内容请求。

例如,你可以使用以下脚本映射配置来启用并行支持:

  1. /app1 上的 ASP.NET v1.1 应用程序:
    *.aspx -> d:\WINDOWS\Microsoft.NET\Framework\v1.1.4322\aspnet_isapi.dll
  2. /app2 上的 ASP.NET v2.0 应用程序:
    *.aspx -> d:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\aspnet_isapi.dll

应用程序收到 *.aspx 请求时,IIS 加载指定的 aspnet_isapi.dll,这会将正确的 CLR 版本加载到工作进程中,并处理该请求。

ASP.NET 也提供以下方式来配置并行脚本映射:

  1. ASP.NET MMC 扩展。 选取要使用的 ASP.NET 版本时,该扩展自动为应用程序配置指向正确的 aspnet_isapi.dll 版本的脚本映射。
  2. ASP.NET aspnet_regiis.exe。 使用 –i 和 –r 选项来安装指向相应 ASP.NET 版本的脚本映射。

遗憾的是,由于只能将一个 CLR 版本加载到一个工作进程中这一限制,你必须确保使用两个不同版本 ASP.NET 的两个应用程序永不配置为存在于同一个应用程序池内。 出现这种常见错误时,第一个请求加载相应 aspnet_isapi.dll 的 CLR,同一个应用程序池内对另一个版本的后续请求将失败。

IIS 识别到该应用程序池为你选择用于运行应用程序的 ASP.NET 版本。 因此,在应用程序池配置中明确配置了在应用程序池中加载的 CLR/ASP.NET 版本。 在默认情况下,IIS 在加载工作进程时预加载此设置所指定的 CLR(除非版本配置为空)。

因为应用程序池为 .NET Framework 版本控制边界,所以你可以通过以下操作更改 ASP.NET 应用程序的版本:

  1. 将应用程序移动到使用所需 ASP.NET 版本的应用程序池。
    应用程序默认使用名为“DefaultAppPool”的默认应用程序池(该池在集成模式下运行 ASP.NET v2.1)。 将应用程序移动到“经典 .NET AppPool”(以在 ASP.NET v2.1 经典模式下运行),或移动到你选择的另一个应用程序池。
  2. 将应用程序在其中运行的应用程序池配置为使用所需 ASP.NET 版本。
    在默认情况下,所有新应用程序池都配置为在集成模式下运行 ASP.NET v2.1。

注意

请勿使用 aspnet_regiis 的 /i 或 /r 选项来为特定应用程序配置或全局配置 ASP.NET 版本。

IIS 使用预先设定的处理程序映射,根据应用程序池的配置 CLR 版本和托管集成模式,自动选择正确的处理程序映射集(在经典模式下选择为 ASPNET_isapi.dll,在集成模式下直接选择为托管的处理程序类型)。 ASP.NET 2.0 设置在服务器级别安装这些处理程序映射。

利用集成 ASP.NET 模式

在 IIS 上,ASP.NET 应用程序默认在集成模式下运行。 但,为了利用更紧密的集成提供的优势,你必须对应用程序配置进行一些修改。 你也可以开发新的 ASP.NET 组件,这些组件利用集成模式为应用程序提供强大的功能。

为所有类型的内容启用 ASP.NET 服务

在集成模式下,ASP.NET 模块提供的服务可用于所有内容类型的请求。 然而,ASP.NET 模块默认配置为只针对 ASP.NET 内容的请求执行,以实现后向兼容性。

为此,在服务器级别的配置部分中,向每个 ASP.NET 模块附加一个 managedHandler 前提条件:

<modules> 
     <add name="FormsAuthentication" type="System.Web.Security.FormsAuthenticationModule" 
             preCondition="managedHandler" />
     <add name="UrlAuthorization" type="System.Web.Security.UrlAuthorizationModule" 
             preCondition="managedHandler" />
    ...
</modules>

前提条件是一项规则,规定服务器评估每个请求来确定是否执行某个模块。 managedHandler 前提条件仅对内容类型映射到 ASP.NET 处理程序的请求为真。

如果要将 ASP.NET 模块提供的功能应用于所有请求,请删除模块条目,然后在应用程序的根 Web.config 文件中在没有前提条件的情况下重新添加它。

若要为整个应用程序启用 ASP.NET 窗体身份验证,请按如下所示修改 Web.config 文件:

<modules> 
    <remove name="FormsAuthentication" /> 
    <add name="FormsAuthentication" type="System.Web.Security.FormsAuthenticationModule" /> 
    …
</modules>

进行此项更改后,FormsAuthentication 模块针对应用程序中的所有请求执行。 这样,你就能使用窗体身份验证保护所有应用程序内容。 有关如何利用集成模式为所有应用程序内容提供窗体身份验证的详细信息,请参阅利用集成管道模式演练

还可以使用快捷方式使所有托管 (ASP.NET) 模块都能针对所有应用程序中的所有请求运行,无论 managedHandler 前提条件如何。 如果要使所有托管模块都能针对所有请求运行,而无需配置每个模块条目以删除 managedHandler 前提条件,请使用 部分中的 runAllManagedModulesForAllRequests<modules> 属性:

<modules runAllManagedModulesForAllRequests="true" />

使用此属性时,managedHandler 前提条件不起作用,所有托管模块都将针对所有请求运行。

尝试启用其他 ASP.NET 模块来应用于整个应用程序。 例如,使用 ASP.NET 输出缓存模块来输出缓存 ASP 页面,或使用 URL 授权和角色管理器来为照片配置访问控制规则。 或者,开发自己的模块,用于将 ASP.NET 的强大功能引入整个网站。

构建功能更强大的 ASP.NET 服务

在集成模式下,ASP.NET 模块向所有内容应用其服务。 此外,因为 ASP.NET 模块在集成模式下的统一请求处理管道中执行,所以它们订阅相同的请求处理阶段并执行与本机服务器模块相同的任务。 同时,ASP.NET API 基本保持不变,一些关键新增功能可解锁以前不可用的功能。

运行时保真度

在集成模式下,向模块公开的 ASP.NET 请求处理阶段直接连接到 IIS 管道的相应阶段。 完整管道包含以下阶段,这些阶段在 ASP.NET 中以 HttpApplication 事件的形式公开:

  1. BeginRequest。 请求处理开始。
  2. AuthenticateRequest。 请求已经过身份验证。 IIS 和 ASP.NET 身份验证模块订阅此阶段以执行身份验证。
  3. PostAuthenticateRequest
  4. AuthorizeRequest。 请求已获授权。 IIS 和 ASP.NET 授权模块检查经过身份验证的用户是否有权访问请求的资源。
  5. PostAuthorizeRequest
  6. ResolveRequestCache。 缓存模块检查缓存中是否存在对此请求的响应,并返回它,而不是继续进行执行路径的其余操作。 ASP.NET 输出缓存和 IIS 输出缓存这两个功能都执行。
  7. PostResolveRequestCache
  8. MapRequestHandler。 此阶段在 ASP.NET 内部,用于确定请求处理程序。
  9. PostMapRequestHandler
  10. AcquireRequestState。 检索请求执行所需的状态。 ASP.NET 会话状态和配置文件模块获取其数据。
  11. PostAcquireRequestState
  12. PreExecuteRequestHandler。 执行处理程序之前的任何任务。
  13. ExecuteRequestHandler。 请求处理程序执行。 提供了 ASPX 页面、ASP 页面、CGI 程序和静态文件。
  14. PostExecuteRequestHandler
  15. ReleaseRequestState。 保存请求状态更改,并在此清理状态。 ASP.NET 会话状态和配置文件模块使用此阶段来进行清理。
  16. PostReleaseRequestState
  17. UpdateRequestCache。 响应存储在缓存中供将来使用。 ASP.NET 输出缓存和 IIS 输出缓存模块执行,以将响应保存到其缓存中。
  18. PostUpdateRequestCache
  19. LogRequest。 此阶段记录请求的结果,并且即使发生错误,也保证执行。
  20. PostLogRequest
  21. EndRequest。 此阶段执行任何最终请求清理,并且即使发生错误,也保证执行。

通过使用熟悉的 ASP.NET API,在与 IIS 模块相同的阶段执行的能力使先前只能在本机 ISAPI 筛选器和扩展中访问的任务现可在托管代码中访问。

例如,现在可以编写执行以下操作的模块:

  1. 在进行任何处理(例如,重写 URL 或执行安全筛选)之前截获请求。
  2. 替换内置身份验证模式。
  3. 修改传入的请求内容,例如请求头。
  4. 筛选所有内容类型的传出响应。

有关如何使用自定义 ASP.NET 身份验证模块扩展 IIS 的很好的示例,请参阅使用 .NET 开发 IIS 7 及更高版本的模块

扩展的 ASP.NET API

ASP.NET API 向后兼容以前的版本,这使现有模块无需修改就能在 IIS 7 及更高版本中工作,也使它们无需更改代码就能应用于所有应用程序内容。

但,为 IIS 集成模式编写的模块可利用一些额外的 API 来启用以前不可用的关键请求处理方案。

新的 HttpResponse.Headers 集合使模块能检查并操作其他应用程序组件生成的响应头。 例如,你可以更改 ASP 页面生成的 Content-Type 标头,以在浏览器中弹出一个下载对话框。 HttpResponse 类上的 Cookie 集合也得到了增强,这样就能修改在请求处理过程中生成的 Cookie,即使这些 Cookie 是在 ASP.NET 外创建的。

HttpRequest.Headers 集合已启用写入,这使模块能操作传入的请求头。 使用此集合更改其他服务器组件的行为,例如:可以通过动态地更改 Accept-Language 标头的值,强制已本地化的应用程序使用一种不同的语言来响应客户端。

最后,HttpRequest.ServerVariables 集合现在可写入,这样就能设置 IIS 服务器变量。 ASP.NET 模块使用此功能更改 IIS 提供的值,并创建新的服务器变量,这些变量对其他应用程序框架(如 PHP)可见。

运行时集成

除了新 API 以外,集成模式还使现有 ASP.NET 功能能够为应用程序提供更多价值。

ASP.NET 提供的跟踪设施(包括 System.Diagnostics.Trace 类和 ASP.NET 页面跟踪功能)现在向 IIS 跟踪系统发出跟踪信息。 这使 IIS 和 ASP.NET 组件在请求处理过程中生成的跟踪信息能放置在单个日志文件中,有助于更好地诊断服务器问题。

ASP.NET 响应筛选功能(能在 Stream 接口进入客户端前使用该接口来更改响应)得到增强,能筛选非 ASP.NET 内容。 因此,你可以对所有服务器内容使用 ASP.NET 筛选,或者仅针对要处理的内容的请求选择性地安装筛选器。 借助此功能,可以编写在站点的响应中注入或审查某些内容的筛选功能,而不管此内容是来自 ASPX 页面还是来自静态文件。

总结

IIS 7 及更高版本中的 ASP.NET 集成发掘出 ASP.NET 的全部潜力,使开发人员能借助 ASP.NET 和 .NET Framework 的丰富易用的功能来创建功能强大的服务器组件。 若要详细了解如何利用现有应用程序的 ASP.NET 集成模式,请参阅利用集成管道模式。 有关如何生成新 ASP.NET 组件以扩展服务器的信息,请参阅使用 .NET 开发 IIS 7 及更高版本的模块