IIS 7.0 及更高版本中的配置系统基于分布式 xml 文件,其中包含 IIS、ASP.NET 和其他组件的配置;配置系统的灵活性还可让用户在多个级别设置配置,包括服务器、站点和应用程序级别。 站点和应用程序级别的配置与 web.config 文件中的 ASP.NET 配置共存。
新配置系统的一个特点是配置可以轻松扩展。 只需做出一些简单的更改,就可以将自定义配置部分直接集成到配置系统中,并使用现有的管理 API 来操作这些设置。 只需在 XML 架构文件中定义新的配置部分,然后将其放入 IIS 架构目录 %windir%\system32\inetsrv\config\schema
,即可扩展配置。 最后,新的配置部分必须注册到 IIS 全局配置文件中。
本文将逐步讲解一个使用基本配置扩展性和一些更高级方案的示例。 尽管使用的示例是人为设计的,但它足以演示配置扩展性的强大之处。
先决条件
需要满足许多先决条件才能学习本文。 其中包括:
- 默认安装了 IIS 7.0 或更高版本。 如果未安装 IIS,请通过打开服务器管理器并添加 Web 服务器 (IIS) 角色来安装。
- 确保已安装 .NET Framework SDK。 如果尚未安装 SDK,请从 https://www.microsoft.com/downloads 获取它
- 使用 SDK 的 bin 目录下的一些工具。 使用开始菜单中的 SDK 命令提示符或将 bin 目录添加到路径(例如
%systemdrive%\Program Files\Microsoft.NET\SDK\v2.0\Bin
) - 使用提升的权限从命令提示符运行所有命令。 右键单击开始菜单中的“SDK 命令提示符”(或“命令提示符”),然后选择“以管理员身份运行”。
配置扩展性 - 基础知识
概述
为了演示一些基本的配置扩展性功能,我们将使用自定义日志记录模块的人为设计示例。 模块本身并不特殊,它只会处理内置的 IIS 日志记录事件 - LogRequest - 并将日志条目写入磁盘上的文件中;请将其视为 IIS 日志记录的基本版本。
配置扩展性开始发挥作用,因为模块需要知道在何处记录信息。 模块必须有一个存储其配置的自定义配置部分 - 在本例中为日志文件位置。
步骤 1 - 架构文件
添加新配置部分的第一步是定义该部分。 在 xml 中定义部分架构,并将文件放入 %windir%\system32\inetsrv\config\schema
目录中。
创建名为 simpleLogging_Schema.xml 的 xml 文件,并将以下内容放入其中:
<configSchema>
<sectionSchema name="system.webServer/simpleLogging">
<attribute name="logfileDirectory" type="string"
defaultValue="%systemdrive%\inetpub\logs\simpleLogs" expanded="true" encrypted="false" />
</sectionSchema>
</configSchema>
上述架构执行两项操作。 首先,它使用 <sectionSchema> 元素定义一个名为“simpleLogging”的新配置部分。 其次,它定义新配置部分的一个属性(名为“logfileDirectory”)。
从架构文件中可以看到,该属性是字符串,配置系统不会对其进行加密。 expanded="true" 告知配置系统在使用环境变量时自动扩展该变量。 如果未在 %windir%\system32\inetsrv\config\schema
目录中创建该文件,现在请将其移动到该目录。
接下来,创建为“logfileDirectory”指定的默认目录,因为它可能不存在于计算机上。 从命令行运行以下命令以创建目录:
md %systemdrive%\inetpub\logs\simpleLogs
Windows 组 IIS_IUSRS 必须对该目录具有写入权限,以便步骤 4 中创建的 SimpleLogging 模块可以向该目录写入日志文件。 在命令行处运行以下命令:
icacls %systemdrive%\inetpub\logs\simpleLogs /grant BUILTIN\IIS_IUSRS:RW
有关架构的详细信息
尽管就本示例而言步骤 1 已完成,但讨论架构文件仍然是合适的。 在上面的架构中,我们只创建了一个位于 system.webServer 下的新配置部分 simpleLogging,并指定了一个自定义属性。 但是,你可以使用集合、元素和属性轻松创建更复杂的自定义配置。 以下列表显示了一些示例,但最好的学习方法是查看 IIS 配置的架构文件。 在 %windir%\system32\inetsrv\config\schema\IIS\_schema.xml
中可以找到它。
attribute
架构信息:
<attribute name="" [String, Required] [XML name of the attribute] type="" [bool|enum|flags|uint|int|int64|string|timeSpan, Required] [Runtime type] required="false" [bool] [Indicates if it must be set] isUniqueKey="false" [bool] [Serves as the collection key] isCombinedKey="false" [bool] [Part of a multi-attribute key] defaultValue="" [String] [Default value or comma-delimited flags] encrypted="false" [bool] [Indicates if the value persisted is encrypted] expanded="false" [bool] [Environment variables are expanded when read] allowInfinite="false" [bool] [Indicates if "Infinite" can be set] timeSpanFormat="string" [string|seconds|minutes] [hh:mm:ss or number] validationType="" [See validation below] validationParameter="" [See validation below] />
示例:
<configSchema> <sectionSchema name="system.webServer/simpleLogging"> <attribute name="logfileDirectory" type="string" /> </sectionSchema> </configSchema>
element
架构信息:
<element name="" [String, Required] [XML name of the element] isCollectionDefault="false" [bool] [Indicates if default values are held for other elements in this collection] />
示例:
<configSchema> <sectionSchema name="system.webServer/simpleLogging"> <element name="logfile"> <attribute name="fileLocation" type="string" /> </element> </sectionSchema> </configSchema>
collection
架构信息:
<collection addElement="" [String] [Name of Add directive, if supported] removeElement="" [String] [Name of Remove directive, if supported] clearElement="" [String] [Name of Clear directive, if supported] defaultElement="" [applicationDefaults|applicationPoolDefaults|siteDefaults|virtualDirectoryDefaults] mergeAppend="true" [bool] [Indicates whether or not deepest set values are appended] allowDuplicates="false" [bool] [Indicates if multiple elements may have the same key] allowUnrecognizedAttributes="false" [bool] [Indicates if non-schema attributes are ok] />
示例:
<configSchema> <sectionSchema name="system.webServer/simpleLogging"> <collection addElement="add"> <attribute name="logfileDirectory" type="string" /> </collection> </sectionSchema> </configSchema>
步骤 2 – 注册新部分
定义新部分后,请向配置系统告知有关该部分的信息。 在 %windir%\system32\inetsrv\config\applicationHost.config
文件中注册新部分。 打开该文件并注册 simpleLogging 部分,如下所示:
<configSections>
...
<sectionGroup name="system.webServer">
<section name="simpleLogging"/>
...
</sectionGroup>
</configSections>
此步骤已完成。 该部分已定义并已注册。
若要检查该部分是否已正确注册,请从命令行运行以下命令:
%windir%\system32\inetsrv\appcmd list config –section:system.webServer/simpleLogging
如果到目前为止一切顺利,则会显示配置部分,并且你会看到如下内容:
<system.webServer>
<simpleLogging />
</system.webServer>
步骤 3 – 设置配置
注册该部分后,请像使用 web.config 文件进行任何其他配置一样设置配置,或者使用 %windir%\system32\inetsrv\
中的 appcmd.exe 工具设置配置。 还可以使用任何配置 API 来设置配置。 还有一种做法是通过新的 IIS 管理 UI 设置配置,方法是创建 UI 模块并调用配置 API 来设置配置。
我们暂时通过将配置添加到默认 IIS 网站(安装在 %systemdrive%\inetpub\wwwroot\
处,在默认 IIS 配置中名为“默认网站”)的新 web.config 文件来设置配置。 创建名为 web.config 的文件并添加以下内容:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<simpleLogging logfileDirectory="%systemdrive%\inetpub\logs\simpleLogs" />
</system.webServer>
</configuration>
可以从命令行使用以下命令来实现相同的效果:
%windir%\system32\inetsrv\appcmd set config "Default Web Site" –section:system.webServer/simpleLogging
/logfileDirectory:""%"systemdrive"%"\inetpub\logs\simpleLogs"
运行以下命令列出“默认网站”的配置:
%windir%\system32\inetsrv\appcmd list config "Default Web Site" –section:system.webServer/simpleLogging
输出如下所示:
<system.webServer>
<simpleLogging logfileDirectory="%systemdrive%\inetpub\logs\simpleLogs" />
</system.webServer>
注意
如果为“logfileDirectory”指定的目录不存在,现在请创建它。 Windows 组 IIS_IUSRS 必须对该目录具有写入权限,以便下一步骤中创建的 SimpleLogging 模块可以向该目录写入日志文件。 上面的步骤 1 显示了用于在默认目录上设置正确权限的命令行命令。 如果创建了不同的目录,请使用类似的命令。
步骤 4 – SimpleLogging 模块
在此阶段,我们已使用客户的 simpleLogging 配置部分扩展了配置系统。 我们通过创建模块并演示如何使用其中的自定义配置,全面了解了基本配置扩展性。
我们将创建一个强命名的 .NET 程序集,IIS 中的所有网站都可以使用该程序集。 对于本部分,我们必须使用 .NET SDK 中的一些工具;如果尚未安装,请从 www.microsoft.com/downloads 网站下载。
所需的步骤包括:
创建一个工作目录并将其打开。
创建 SimpleLoggingModule.cs 文件并使用文本编辑器在其中添加以下代码:
using System; using System.Web; using System.Web.Hosting; using System.IO; using Microsoft.Web.Administration; namespace ConfigurationExtensibility { public class SimpleLoggingModule : IHttpModule { private string GetlogfileDirectory(HttpContext context) { ConfigurationSection section = WebConfigurationManager.GetSection( context, "system.webServer/simpleLogging"); return (string)section["logfileDirectory"]; } public void Init(HttpApplication context) { context.LogRequest += new EventHandler(LogRequest_EventHandler); } private void LogRequest_EventHandler(object sender, EventArgs e) { HttpApplication application = (HttpApplication)sender; LogRequest(application.Context); } private void LogRequest(HttpContext context) { string logfileDirectory = GetlogfileDirectory(context); if (Directory.Exists(logfileDirectory) == false) { Directory.CreateDirectory(logfileDirectory); } string logfile = Path.Combine(logfileDirectory, DateTime.Now.ToString("yyyyMMdd") + ".log"); string ogline = string.Format("{0}\t{1}\t{2}\t{3}\t{4}\t{5}\t{6}\t{7}", DateTime.Now, context.Request.HttpMethod, context.Request.UserHostAddress, context.Request.Url.ToString(), context.Request.ServerVariables["LOGON_USER"], context.Request.UserAgent, context.Response.StatusCode, HostingEnvironment.SiteName); File.AppendAllText(logfile, ogline + Environment.NewLine); } public void Dispose() { } } }
我们必须将其设为强命名模块,以便 IIS 将其用作所有站点的全局模块。 首先创建强名称密钥文件。 打开命令提示符,将目录切换到包含 SimpleLoggingModule.cs 文件的目录。 然后运行以下命令(确保 .NET Framework SDK 的 bin 目录位于路径中):
sn.exe /k keyFile.snk
如果正常运行,sn.exe 的输出会显示类似于“写入 keyFile.snk 的密钥对”的内容
现在编译该文件并创建一个 DLL。 从命令提示符处运行以下命令:
%windir%\Microsoft.NET\Framework\v2.0.50727\csc.exe /t:library SimpleLoggingModule.cs /r:System.Web.dll /r:%windir%\system32\inetsrv\Microsoft.Web.Administration.dll /keyfile:keyFile.snk
接下来,将编译的程序集 (SimpleLoggingModule.dll) 放入全局程序集缓存中。 从命令提示符处运行以下命令:
gacutil.exe /i SimpleLoggingModule.dll
现在我们必须将模块添加到 IIS 可以使用的模块列表中。 但在此之前,我们必须获取刚刚创建的程序集的全名。 在命令行处运行以下命令:
gacutil.exe /l SimpleLoggingModule
这会输出如下所示的内容:
SimpleLoggingModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=652a8d02f48e4288, processorArchitecture=MSIL
将模块添加到 IIS 可以使用的模块列表中。 运行下面的命令。 但是,请确保将变量替换为最后一个命令的输出。
%windir%\system32\inetsrv\appcmd add module /name:"SimpleLoggingModule" /type:"ConfigurationExtensibility.SimpleLoggingModule, SimpleLoggingModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=652a8d02f48e4288"
这会将所需的配置条目添加到 applicationHost.config 文件(IIS 的全局配置文件)中。
该过程已完成。 使用自定义配置的自定义模块已设置。 剩下的操作就是对其进行测试。 启动浏览器并导航到 http://localhost/. 会看到如下内容:
如果出现错误,请确保已授予 IIS_IUSRS 组写入该目录的权限。
打开 %systemdrive%\inetpub\logs\simpleLogs
(或在配置中使用的任何目录),你将获得一个以当天日期命名的 .log 文件。 打开该文件,你会看到如下所示的内容:
试着运行配置以确保其正常工作。 尝试从 web.config 文件中删除 simpleLogging 部分,并检查日志是否位于默认位置(只需确保 ASPNET 用户具有正确的权限)。
注意
我们刚刚创建的模块仅用于演示目的,不应在生产环境中使用。 如果有多个请求同时尝试写入日志条目,则会失败。
配置扩展性 - 更高级方案
概述
上一部分探讨了配置扩展性的基本作用 – 它只是使用架构扩展配置。 但是,在扩展配置时还可以使用更强大的功能。
首先,可以扩展配置以使用 COM 对象来检索配置,从而可以随时随地存储配置信息,而不必担心配置 API 无法读取它。
其次,可以定义对配置进行操控和操作的方法。 然后可以使用现有的配置 API 调用这些方法。 这两项功能共同为生成自定义配置扩展提供了强大的支持。
本部分首先介绍如何修改本文第一部分所述的 simpleLogging 自定义配置,以使用 COM 组件检索配置值。 然后,介绍如何添加由执行操作的 COM 组件支持的配置方法。
扩展配置 - 由 COM 支持的属性
本部分将扩展架构,以获得名为“logfileCount”的属性。 此配置属性由 .NET 程序集(托管的 dll - 用 C# 编程)支持,该程序集统计日志目录中的日志文件数;同样,这是一个人为设计的方案,但有些人可能觉得它很有用。
注意
不必创建 .NET 组件 - 可以使用任何有效的 COM 组件。
步骤 1 - 创建并注册 .NET COM 组件
首先创建 .NET COM 组件。 创建一个目录用于存储我们要创建的文件以及要生成的组件,然后打开该目录。
创建的组件必须实现 IIS 配置系统通过 COM 公开的一些接口。 若要从 .NET 组件使用 COM 接口,我们必须创建一个互操作 DLL - 这样,IIS 配置系统在必须获取 logfileCount 属性的值时,就可以与该组件通信。 若要创建互操作 DLL,请使用 .NET Framework SDK 中名为 tlbimp.exe 的工具。 安装 Visual Studio 或 .NET SDK 是本文的先决条件。 如果尚未安装,请从 www.microsoft.com/downloads 下载。
下面是创建 .NET COM 组件的步骤:
打开命令行提示符,并切换到你创建的用于存储文件的目录。 确保 .NET Framework 的 bin 目录位于路径中,然后在命令行处运行以下命令:
tlbimp %windir%\system32\inetsrv\nativerd.dll /keyfile:keyFile.snk
tlbimp.exe 工具将创建一个名为 AppHostAdminLibrary.dll 的文件 - 这就是我们需要的互操作 DLL。
在之前创建的目录中创建 ConfigurationExtensibility.cs 文件,然后使用文本编辑器将以下 C# 代码复制到该文件中:
using System; using System.IO; using System.Runtime.InteropServices; using AppHostAdminLibrary; namespace ConfigurationExtensibility { [ComVisible(true)] public class SimpleLoggingExtension : IAppHostPropertyExtension { public void ProvideGetProperty(IAppHostElement pElement, IAppHostProperty pProperty) { switch(pProperty.Name) { case "logfileCount": string logDirectory = (string) pElement.Properties["logfileDirectory"].Value; if(Directory.Exists(logDirectory)) pProperty.Value = Directory.GetFiles(logDirectory, "????????.log").Length; else pProperty.Value = 0; break; } } } }
注意
我们有一个实现 IAppHostPropertyExtension 接口的类。 代码本身仅读取 logfileDirectory 属性来获取日志文件目录,然后统计与 SimpleLoggingModule 创建的日志文件的文件名模式匹配的所有文件。
从命令行运行以下命令,以生成组件:
%windir%\Microsoft.NET\Framework\v2.0.50727\csc.exe /t:library /r:AppHostAdminLibrary.dll ConfigurationExtensibility.cs /keyfile:keyFile.snk
现已创建 .NET COM 组件 – ConfigurationExtensibility.dll。
注册刚刚创建的托管 COM 组件。 在命令提示符下运行以下命令:
%windir%\Microsoft.NET\Framework\v2.0.50727\regasm.exe /register /codebase ConfigurationExtensibility.dll
这会在注册表中注册该 COM 组件。 你已创建并注册了配置系统可以使用的 .NET COM 组件。
步骤 2 - 更新架构文件
接下来,修改之前创建的 simpleLogging_Schema.xml 文件。 打开该文件,并如下所示对其进行更改(新属性以粗体显示):
<configSchema>
<sectionSchema name="system.webServer/simpleLogging">
<attribute name="logfileDirectory" type="string"
defaultValue="%systemdrive%\inetpub\logs\simpleLogs\" expanded="true" encrypted="false" />
<attribute name="logfileCount" type="int" extension="ConfigurationExtensibility.SimpleLoggingExtension" />
</sectionSchema>
</configSchema>
步骤 3 - 测试
现在一切应该都在正常进行 – 剩下的操作就是测试了。 若要测试扩展,请使用一个简单的脚本。 创建 SimpleLoggingTest.vbs 文件并输入以下文本:
Dim adminManager, section
Set adminManager = WScript.Createobject("Microsoft.ApplicationHost.AdminManager")
Set section = adminManager.GetAdminSection("system.webServer/simpleLogging",
"MACHINE/WEBROOT/APPHOST/Default Web Site")
WScript.Echo(section.Properties.Item("logfileCount").Value)
在此阶段,在如前所述测试 SimpleLoggingModule 后,应该生成了一个日志文件。 从命令行运行脚本。 你会看到输出为 1。
扩展配置 - 由 COM 支持的方法
最后,本文将探讨如何使用方法扩展配置。 配置方法是配置系统可以调用的、用于完成工作的操作,例如修改配置或删除日志文件 – 这正是此方法将要执行的操作。 对于此示例,我们将添加一个方法来删除 SimpleLoggingModule 创建的所有日志文件。
步骤 1 - 代码
首先,为该方法添加所需的代码。 打开之前创建的 ConfigurationExtensibility.cs 文件,并如下所示将其更新(新代码以粗体显示):
using System; using System.IO; using System.Runtime.InteropServices; using AppHostAdminLibrary; namespace ConfigurationExtensibility { [ComVisible(true)] public class SimpleLoggingExtension : IAppHostPropertyExtension, IAppHostMethodExtension { public void ProvideGetProperty(IappHostElement pElement, IappHostProperty pProperty) { switch(pProperty.Name) { case "logfileCount": string logDirectory = (string) pElement.Properties["logfileDirectory"].Value; if(Directory.Exists(logDirectory)) pProperty.Value = Directory.GetFiles(logDirectory, "????????.log").Length; else pProperty.Value = 0; break; } } public void ProvideMethod(IappHostMethod pMethod, IappHostMethodInstance pMethodInstance, IappHostElement pElement) { switch(pMethod.Name) { case "deleteLogs": string logDirectory = (string) pElement.Properties["logfileDirectory"].Value; if(Directory.Exists(logDirectory)) { foreach(string logFile in Directory.GetFiles(logDirectory, "????????.log")) { File.Delete(logFile); } } break; } } } }
注意
我们已实现 IAppHostMethodExtension 接口。 此接口有一个名为 ProvideMethod 的方法,此方法在逻辑上提供方法。 当某人调用该方法时(请参阅“步骤 3”了解如何执行此操作),配置系统会调用 ProvideMethod 并传递参数,其中一个参数包含被调用方法的名称;在上面的代码中,我们只会处理名为“deleteLogs”的方法。
使用以下命令再次生成项目:
%windir%\Microsoft.NET\Framework\v2.0.50727\csc.exe /t:library /r:AppHostAdminLibrary.dll ConfigurationExtensibility.cs /keyfile:keyFile.snk
步骤 2 - 更新架构
接下来,将新方法告知架构。 在此阶段,你应该相当熟悉 simpleLogging_Schema.xml 文件,因此请再次打开它,并如下所示对其进行更改:
<configSchema>
<sectionSchema name="system.webServer/simpleLogging">
<attribute name="logfileDirectory" type="string"
defaultValue="c:\inetpub\logs\simpleLogs\" expanded="true" encrypted="false" />
<attribute name="logfileCount" type="int" extension="ConfigurationExtensibility.SimpleLoggingExtension" />
<method name="deleteLogs" extension="ConfigurationExtensibility.SimpleLoggingExtension" />
</sectionSchema>
</configSchema>
此项更改定义了一个名为“deleteLogs”的新方法,并告知配置要在何处查找该方法。
步骤 3 - 测试
最后,检查该方法是否可正常工作。 为此,一种快速简单的办法是编写一个简单的 VB 脚本。 以下示例脚本输出 logfileCount,然后调用我们的方法并输出 logfileCount。 只需更新之前创建的 SimpleLoggingTest.vbs 文件并输入以下内容即可:
Dim adminManager, section
Set adminManager = WScript.Createobject("Microsoft.ApplicationHost.AdminManager")
Set section = adminManager.GetAdminSection("system.webServer/simpleLogging", "MACHINE/WEBROOT/APPHOST/Default Web Site")
WScript.Echo(section.Properties.Item("logfileCount").Value)
section.Methods.Item("deleteLogs").CreateInstance().Execute()
WScript.Echo(section.Properties.Item("logfileCount").Value)
从命令行运行脚本,你将看到以下输出:
1
0
上面简要概述了如何提供由 COM 组件支持的新配置和配置方法。 你可能已发现,使用此方法扩展配置是非常有效的做法。
配置扩展性 - 扩展现有配置
配置扩展性的最后一个方面是能够扩展现有配置部分(例如 system.webServer/sites 部分),或扩展在前面两个部分中创建的 system.webServer/simpleLogging 部分。
扩展现有配置部分就像创建新配置部分一样简单。 只需将架构定义为 xml,并将架构文件放在 %windir%\system32\inetsrv\config\schema\
目录中即可。 你应该对此操作很熟悉,因为我们之前多次这样做过。
扩展“站点”配置
为了更好地演示如何扩展现有配置部分,我们将扩展 system.applicationHost/sites 部分 - 用于定义站点的配置部分。 我们将通过添加“owner”属性和“ownerEmail”属性来扩展站点部分。 在一个机箱中托管多个站点并希望跟踪谁拥有不同站点时,这种属性非常有用。
首先创建新的架构文件。 在 %windir%\system32\inetsrv\config\schema\
目录中创建 siteExtension_schema.xml 文件并输入以下文本:
<configSchema>
<sectionSchema name="system.applicationHost/sites">
<collection addElement="site">
<attribute name="owner" type="string" />
<attribute name="ownerEmail" type="string" />
</collection>
</sectionSchema>
</configSchema>
扩展现有部分的架构时,只需创建一个 <sectionSchema>
元素,并将 name 属性设置为与现有部分相同。 在上面的架构文件中,我们定义了一个名为“system.applicationHost/sites”的 <sectionSchema> - 这与 Schema 目录中 IIS_Schema.xml 文件中的 sectionSchema 名称相同。
通过添加“owner”和“ownerEmail”属性的值来测试我们的修改,然后检查配置文件以查看更改。 只需从命令行运行以下命令:
%windir%\system32\inetsrv\appcmd set site "Default Web Site" /owner:"John Contoso" /ownerEmail:"john@contoso.com"
若要查看配置是否已应用,请运行以下命令并检查输出:
%windir%\system32\inetsrv\appcmd list site "Default Web Site" /config
输出应如下所示:
<system.applicationHost>
<sites>
...
<site name="Default Web Site" id="1" siteOwner="John Contoso" siteOwnerEmail="john@contoso.com">
...
...
</site>
</sites>
</system.applicationHost>
注意
如果现在浏览到 http://localhost/
,你可能会收到服务器 500.19 错误消息。 这是一个已知问题,将在 IIS 的后续版本中予以解决。 若要解决此问题,请从命令行运行“iisreset”。
有关配置扩展性的介绍到此结束。 但愿你在阅读前面的示例后,能够灵活地使用配置扩展性。