ASP.NET 的配置生成器

作者 :斯蒂芬·莫洛伊里克·安德森

配置生成器为 ASP.NET 应用提供新式和敏捷的机制,用于从外部源获取配置值。

配置生成器:

  • 可在 .NET Framework 4.7.1 及更高版本中使用。
  • 提供用于读取配置值的灵活机制。
  • 在应用进入容器和云重点环境时,解决应用的某些基本需求。
  • 可以通过从以前不可用的源(例如 Azure 密钥库 和环境变量)中绘制来改进配置数据的保护。

键/值配置生成器

配置生成器可以处理的常见方案是为遵循键/值模式的配置节提供基本键/值替换机制。 ConfigurationBuilders 的 .NET Framework 概念不限于特定的配置部分或模式。 但是,(github,NuGet)中的Microsoft.Configuration.ConfigurationBuilders许多配置生成器在键/值模式中工作。

键/值配置生成器设置

以下设置适用于所有键/值配置生成器。Microsoft.Configuration.ConfigurationBuilders

模式

配置生成器使用键/值信息的外部源来填充配置系统的所选键/值元素。 具体而言, <appSettings/> 这些和 <connectionStrings/> 部分从配置生成器处获得特殊处理。 构建者以三种模式工作:

  • Strict - 默认模式。 在此模式下,配置生成器仅对以已知键/以值为中心的配置部分进行操作。 Strict 模式枚举节中的每个键。 如果在外部源中找到匹配的键:

    • 配置生成器将生成的配置部分中的值替换为外部源中的值。
  • Greedy - 此模式与模式密切相关 Strict 。 不限于原始配置中已存在的密钥:

    • 配置生成器会将外部源中的所有键/值对添加到生成的配置部分。
  • Expand - 在分析到配置节对象之前,先对原始 XML 进行操作。 可以将其视为字符串中标记的扩展。 与模式 ${token} 匹配的原始 XML 字符串的任何部分都是令牌扩展的候选项。 如果在外部源中找不到相应的值,则不会更改令牌。 此模式下的生成器不限于 <appSettings/> 这些和 <connectionStrings/> 部分。

Web.config 中的以下标记Strict模式下启用 EnvironmentConfigBuilder

<configuration>

  <configSections>
    <section name="configBuilders" 
             type="System.Configuration.ConfigurationBuildersSection, 
             System.Configuration, Version=4.0.0.0, Culture=neutral, 
             PublicKeyToken=b03f5f7f11d50a3a"
             restartOnExternalChanges="false" requirePermission="false" />
  </configSections>

  <configBuilders>
    <builders>
      <add name="MyEnvironment"
           type="Microsoft.Configuration.ConfigurationBuilders.EnvironmentConfigBuilder, 
           Microsoft.Configuration.ConfigurationBuilders.Environment, 
           Version=1.0.0.0, Culture=neutral" />
    </builders>
  </configBuilders>

  <appSettings configBuilders="MyEnvironment">
    <add key="ServiceID" value="ServiceID value from web.config" />
    <add key="ServiceKey" value="ServiceKey value from web.config" />
  </appSettings>

  <connectionStrings configBuilders="MyEnvironment">
    <add name="default" connectionString="Data Source=web.config/mydb.db" />
  </connectionStrings>

以下代码读取<appSettings/><connectionStrings/>前面 web.config 文件中所示的内容:

using System;
using System.Configuration;
using System.Web.UI;

namespace MyConfigBuilders
{
    public partial class About : Page
    {
        public string ServiceID { get; set; }
        public string ServiceKey { get; set; }
        public string ConString { get; set; }

        protected void Page_Load(object sender, EventArgs e)
        {
            ServiceID = ConfigurationManager.AppSettings["ServiceID"];
            ServiceKey = ConfigurationManager.AppSettings["ServiceKey"];
            ConString = ConfigurationManager.ConnectionStrings["default"]
                                            ?.ConnectionString;
        }
    }
}

前面的代码将属性值设置为:

  • 如果未在 环境变量中设置密钥,则 web.config 文件中的值。
  • 环境变量的值(如果已设置)。

例如, ServiceID 将包含:

  • 如果未设置环境变量 ServiceID ,则为“web.config 中的 ServiceID 值”。
  • 环境变量的值 ServiceID (如果已设置)。

下图显示了 <appSettings/> 环境编辑器中上 一个 web.config 文件中设置的键/值:

屏幕截图显示了环境变量编辑器,其中突出显示了 ServiceID 和 ServiceKey 变量。

注意:可能需要退出并重启 Visual Studio 才能查看环境变量中的更改。

前缀处理

键前缀可以简化设置键,因为:

  • .NET Framework 配置复杂且嵌套。
  • 外部键/值源通常是基本和平面的。 例如,环境变量不是嵌套的。

使用以下任一方法通过 <appSettings/> 环境变量注入和 <connectionStrings/> 注入配置:

  • EnvironmentConfigBuilder 默认 Strict 模式下,配置文件中具有相应的密钥名称。 前面的代码和标记采用此方法。 使用此方法时,不能在两者<appSettings/><connectionStrings/>具有相同命名的键。
  • 在模式中使用Greedy具有不同前缀的两EnvironmentConfigBuilder个 s 和 stripPrefix。 使用此方法,应用可以读取 <appSettings/><connectionStrings/> 无需更新配置文件。 下一部分 stripPrefix 演示如何执行此操作。
  • 在模式中使用GreedyEnvironmentConfigBuilder个具有不同前缀的 s。 使用此方法时,不能有重复的键名称,因为密钥名称必须因前缀而异。 例如:
<configuration>

  <configSections>
    <section name="configBuilders"
             type="System.Configuration.ConfigurationBuildersSection, 
             System.Configuration, Version=4.0.0.0, Culture=neutral, 
             PublicKeyToken=b03f5f7f11d50a3a"
             restartOnExternalChanges="false" requirePermission="false" />
  </configSections>

  <configBuilders>
    <builders>
      <add name="AS_Environment" mode="Greedy" prefix="AppSetting_"
           type="Microsoft.Configuration.ConfigurationBuilders.EnvironmentConfigBuilder, 
           Microsoft.Configuration.ConfigurationBuilders.Environment" />
      <add name="CS_Environment" mode="Greedy" prefix="ConnStr_"
           type="Microsoft.Configuration.ConfigurationBuilders.EnvironmentConfigBuilder, 
           Microsoft.Configuration.ConfigurationBuilders.Environment" />
    </builders>
  </configBuilders>

  <appSettings configBuilders="AS_Environment">
    <add key="AppSetting_ServiceID" value="ServiceID value from web.config" />
    <add key="AppSetting_default" value="AppSetting_default value from web.config" />
  </appSettings>

  <connectionStrings configBuilders="CS_Environment">
    <add name="ConnStr_default" connectionString="Data Source=web.config/mydb.db" />
  </connectionStrings>

使用前面的标记,可以使用同一平面键/值源来填充两个不同的部分的配置。

下图显示了环境编辑器中上一个 web.config 文件中设置的<appSettings/><connectionStrings/>键/值:

屏幕截图显示了环境变量编辑器,其中突出显示了AppSetting_default、AppSetting_ServiceID和ConnStr_default变量。

以下代码读取<appSettings/><connectionStrings/>前面 web.config 文件中包含的键/值:

public partial class Contact : Page
{
    public string ServiceID { get; set; }
    public string AppSetting_default { get; set; }
    public string ConString { get; set; }

    protected void Page_Load(object sender, EventArgs e)
    {
        ServiceID = ConfigurationManager.AppSettings["AppSetting_ServiceID"];
        AppSetting_default = ConfigurationManager.AppSettings["AppSetting_default"];
        ConString = ConfigurationManager.ConnectionStrings["ConnStr_default"]
                                     ?.ConnectionString;
    }
}

前面的代码将属性值设置为:

  • 如果未在 环境变量中设置密钥,则 web.config 文件中的值。
  • 环境变量的值(如果已设置)。

例如,使用以前的 web.config 文件、上一环境编辑器映像中的键/值和上一个代码,将设置以下值:

密钥
AppSetting_ServiceID env 变量AppSetting_ServiceID
AppSetting_default 来自 env 的AppSetting_default值
ConnStr_default 从 env ConnStr_default val

stripPrefix

stripPrefix:布尔值,默认值为 false.

前面的 XML 标记将应用设置与连接字符串分开,但要求 web.config 文件中的所有键都使用指定的前缀。 例如,必须将前缀 AppSetting 添加到 ServiceID 密钥(“AppSetting_ServiceID”)。 使用 stripPrefix时,该前缀不会在 web.config 文件中使用。 配置生成器源中需要前缀(例如,在环境中)。我们预计大多数开发人员都将使用 stripPrefix

应用程序通常去除前缀。 以下 web.config 将去除前缀:

<configuration>

  <configSections>
    <section name="configBuilders"
             type="System.Configuration.ConfigurationBuildersSection, 
             System.Configuration, Version=4.0.0.0, Culture=neutral, 
             PublicKeyToken=b03f5f7f11d50a3a"
             restartOnExternalChanges="false" requirePermission="false" />
  </configSections>

  <configBuilders>
    <builders>
      <add name="AS_Environment" mode="Greedy" prefix="AppSetting_" 
           stripPrefix="true"
           type="Microsoft.Configuration.ConfigurationBuilders.EnvironmentConfigBuilder, 
           Microsoft.Configuration.ConfigurationBuilders.Environment, 
           Version=1.0.0.0, Culture=neutral" />
      <add name="CS_Environment" mode="Greedy" prefix="ConnStr_" 
           stripPrefix="true"
            type="Microsoft.Configuration.ConfigurationBuilders.EnvironmentConfigBuilder, 
           Microsoft.Configuration.ConfigurationBuilders.Environment, 
           Version=1.0.0.0, Culture=neutral" />
    </builders>
  </configBuilders>

  <appSettings configBuilders="AS_Environment">
    <add key="ServiceID" value="ServiceID value from web.config" />
    <add key="default" value="AppSetting_default value from web.config" />
  </appSettings>

  <connectionStrings configBuilders="CS_Environment">
    <add name="default" connectionString="Data Source=web.config/mydb.db" />
  </connectionStrings>

在前面的 web.config 文件中, default 密钥同时位于 <appSettings/><connectionStrings/>中。

下图显示了环境编辑器中上一个 web.config 文件中设置的<appSettings/><connectionStrings/>键/值:

屏幕截图显示了环境变量编辑器,其中突出显示了AppSetting_default、AppSetting_ServiceID和ConnStr_default变量。

以下代码读取<appSettings/><connectionStrings/>前面 web.config 文件中包含的键/值:

public partial class About2 : Page
{
    public string ServiceID { get; set; }
    public string AppSetting_default { get; set; }
    public string ConString { get; set; }

    protected void Page_Load(object sender, EventArgs e)
    {
        ServiceID = ConfigurationManager.AppSettings["ServiceID"];
        AppSetting_default = ConfigurationManager.AppSettings["default"];
        ConString = ConfigurationManager.ConnectionStrings["default"]
                                        ?.ConnectionString;
    }
}

前面的代码将属性值设置为:

  • 如果未在 环境变量中设置密钥,则 web.config 文件中的值。
  • 环境变量的值(如果已设置)。

例如,使用以前的 web.config 文件、上一环境编辑器映像中的键/值和上一个代码,将设置以下值:

密钥
ServiceID env 变量AppSetting_ServiceID
default 来自 env 的AppSetting_default值
default 从 env ConnStr_default val

tokenPattern

tokenPattern:字符串,默认值为 @"\$\{(\w+)\}"

Expand生成器的行为在原始 XML 中搜索类似于${token}的标记。 使用默认正则表达式 @"\$\{(\w+)\}"完成搜索。 匹配 \w 的字符集比 XML 更严格,并且许多配置源都允许。 在令牌名称中需要的字符数多@"\$\{(\w+)\}"时使用tokenPattern

tokenPattern:字符串:

  • 允许开发人员更改用于令牌匹配的正则表达式。
  • 没有验证,以确保它是一个格式正确的非危险正则表达式。
  • 它必须包含捕获组。 整个正则表达式必须与整个令牌匹配。 第一个捕获必须是要在配置源中查找的令牌名称。

Microsoft.Configuration.ConfigurationBuilders 中的配置生成器

EnvironmentConfigBuilder

<add name="Environment"
    [mode|prefix|stripPrefix|tokenPattern] 
    type="Microsoft.Configuration.ConfigurationBuilders.EnvironmentConfigBuilder,
    Microsoft.Configuration.ConfigurationBuilders.Environment" />

EnvironmentConfigBuilder

  • 最简单的配置生成器。
  • 从环境中读取值。
  • 没有任何其他配置选项。
  • 属性值 name 是任意的。

注意: 在 Windows 容器环境中,运行时设置的变量仅注入到 EntryPoint 进程环境中。 作为服务或非 EntryPoint 进程运行的应用不会选取这些变量,除非通过容器中的机制注入这些变量。 对于基于 IIS/ASP.NET 的容器,当前版本的 ServiceMonitor.exe 仅在 DefaultAppPool处理此问题。 其他基于 Windows 的容器变体可能需要为非 EntryPoint 进程开发自己的注入机制。

UserSecretsConfigBuilder

警告

切勿在源代码中存储密码、敏感连接字符串或其他敏感数据。 不应将生产机密用于开发或测试。

<add name="UserSecrets"
    [mode|prefix|stripPrefix|tokenPattern]
    (userSecretsId="{secret string, typically a GUID}" | userSecretsFile="~\secrets.file")
    [optional="true"]
    type="Microsoft.Configuration.ConfigurationBuilders.UserSecretsConfigBuilder,
    Microsoft.Configuration.ConfigurationBuilders.UserSecrets" />

在前面的 XML 中, userSecretsFile 路径可以使用 ~/~\。 例如,路径可以编写为 userSecretsFile="~/secrets.file。 有关详细信息, 请参阅 ConfigurationBuilders Utils 类。

此配置生成器提供的功能类似于 ASP.NET 核心机密管理器

UserSecretsConfigBuilder 可用于 .NET Framework 项目,但必须指定机密文件。 或者,可以在项目文件中定义 UserSecretsId 属性,并在正确的位置创建原始机密文件进行读取。 若要使外部依赖项远离项目,机密文件的格式为 XML。 XML 格式是实现详细信息,不应依赖该格式。 如果需要与 .NET Core 项目共享 secrets.json 文件,请考虑使用 SimpleJsonConfigBuilderSimpleJsonConfigBuilder还应将 .NET Core 的格式视为可能更改的实现详细信息。

的配置属性:UserSecretsConfigBuilder

  • userSecretsId - 这是用于标识 XML 机密文件的首选方法。 它的工作方式类似于 .NET Core,它使用 UserSecretsId 项目属性来存储此标识符。 字符串必须是唯一的,它不需要是 GUID。 使用此属性,查找 UserSecretsConfigBuilder 属于此标识符的机密文件的已知本地位置(%APPDATA%\Microsoft\UserSecrets\<UserSecrets Id>\secrets.xml)。
  • userSecretsFile - 指定包含机密的文件的可选属性。 ~该字符可用于开始引用应用程序根目录。 此属性或 userSecretsId 属性是必需的。 如果同时指定了两者, userSecretsFile 则优先。
  • optional:布尔值,默认值 true - 如果找不到机密文件,则阻止异常。
  • 属性值 name 是任意的。

机密文件的格式如下:

<?xml version="1.0" encoding="utf-8" ?>
<root>
  <secrets ver="1.0">
    <secret name="secret key name" value="secret value" />
  </secrets>
</root>

AzureKeyVaultConfigBuilder

<add name="AzureKeyVault"
    [mode|prefix|stripPrefix|tokenPattern]
    (vaultName="MyVaultName" |
     uri="https:/MyVaultName.vault.azure.net")
    [version="secrets version"]
    [preloadSecretNames="true"]
    type="Microsoft.Configuration.ConfigurationBuilders.AzureKeyVaultConfigBuilder,
    Microsoft.Configuration.ConfigurationBuilders.Azure" />

AzureKeyVaultConfigBuilder 读取存储在 Azure 密钥库中的值。

vaultName 是必需的(保管库的名称或保管库的 URI)。 其他属性允许控制要连接到哪个保管库,但仅在应用程序未在使用 Microsoft.Azure.Services.AppAuthentication的环境中运行时才是必需的。 Azure 服务身份验证库可用于尽可能自动从执行环境中选取连接信息。 可以通过提供连接字符串来替代自动选取连接信息。

  • vaultName - 如果未 uri 提供,则为必需。 指定要从中读取密钥/值对的 Azure 订阅中的保管库的名称。
  • uri- 连接到具有指定uri值的其他密钥库提供程序。 如果未指定,Azure (vaultName) 是保管库提供程序。
  • version- Azure 密钥库为机密提供版本控制功能。 如果 version 已指定,则生成器仅检索与此版本匹配的机密。
  • preloadSecretNames - 默认情况下,此生成器在初始化密钥保管库时查询 密钥保管库中的所有 密钥名称。 若要防止读取所有键值,请将此属性设置为 false。 将此设置设置为 false 一次读取机密。 如果保管库允许“获取”访问,但不允许“列出”访问,则一次读取机密非常有用。 注意: 使用 Greedy 模式时, preloadSecretNames 必须是 true (默认值)。

KeyPerFileConfigBuilder

<add name="KeyPerFile"
    [mode|prefix|stripPrefix|tokenPattern]
    (directoryPath="PathToSourceDirectory")
    [ignorePrefix="ignore."]
    [keyDelimiter=":"]
    [optional="false"]
    type="Microsoft.Configuration.ConfigurationBuilders.KeyPerFileConfigBuilder,
    Microsoft.Configuration.ConfigurationBuilders.KeyPerFile" />

KeyPerFileConfigBuilder 是一个基本配置生成器,它使用目录的文件作为值的源。 文件的名称是键,内容是值。 在协调的容器环境中运行时,此配置生成器非常有用。 Docker Swarm 和 Kubernetes 等系统以按文件密钥的方式提供给 secrets 其协调的 Windows 容器。

属性详细信息:

  • directoryPath - 必需。 指定要查找值的路径。 默认情况下,适用于 Windows 的 Docker 机密存储在 C:\ProgramData\Docker\secrets 目录中。
  • ignorePrefix - 排除以此前缀开头的文件。 默认值为“忽略”。
  • keyDelimiter - 默认值为 null. 如果指定,配置生成器将遍历目录的多个级别,并使用此分隔符构建密钥名称。 如果值为此值 null,则配置生成器仅查看目录的顶层。
  • optional - 默认值为 false. 指定如果源目录不存在,配置生成器是否应导致错误。

SimpleJsonConfigBuilder

警告

切勿在源代码中存储密码、敏感连接字符串或其他敏感数据。 不应将生产机密用于开发或测试。

<add name="SimpleJson"
    [mode|prefix|stripPrefix|tokenPattern]
    jsonFile="~\config.json"
    [optional="true"]
    [jsonMode="(Flat|Sectional)"]
    type="Microsoft.Configuration.ConfigurationBuilders.SimpleJsonConfigBuilder,
    Microsoft.Configuration.ConfigurationBuilders.Json" />

.NET Core 项目经常使用 JSON 文件进行配置。 SimpleJsonConfigBuilder 生成器允许在 .NET Framework 中使用 .NET Core JSON 文件。 此配置生成器提供从平面键/值源到 .NET Framework 配置的特定键/值区域的基本映射。 此配置生成器不提供分层配置。 JSON 支持文件类似于字典,而不是复杂的分层对象。 可以使用多层分层文件。 此提供程序 flatten通过在每个级别追加属性名称(用作 : 分隔符)来深度。

属性详细信息:

  • jsonFile - 必需。 指定要从中读取的 JSON 文件。 ~该字符可用于在开始处引用应用根。

  • optional - 布尔值,默认值为 true. 如果找不到 JSON 文件,则防止引发异常。

  • jsonMode - [Flat|Sectional]Flat 是默认值。 如果 jsonModeFlat,JSON 文件是单个平面键/值源。 EnvironmentConfigBuilder并且AzureKeyVaultConfigBuilder也是单个平面键/值源。 SimpleJsonConfigBuilderSectional模式下配置时:

    • JSON 文件在概念上只分为多个字典。
    • 每个字典仅应用于与附加到它们的顶级属性名称匹配的配置节。 例如:
    {
        "appSettings" : {
            "setting1" : "value1",
            "setting2" : "value2",
            "complex" : {
                "setting1" : "complex:value1",
                "setting2" : "complex:value2",
            }
        }
    }

配置生成器顺序

请参阅 aspnet/MicrosoftConfigurationBuilders GitHub 存储库中的 ConfigurationBuilders 执行顺序。

实现自定义键/值配置生成器

如果配置生成器不满足你的需求,则可以编写自定义生成器。 KeyValueConfigBuilder基类处理替换模式和大多数前缀问题。 实现项目只需要:

using Microsoft.Configuration.ConfigurationBuilders;
using System.Collections.Generic;

public class MyCustomConfigBuilder : KeyValueConfigBuilder
{
    public override string GetValue(string key)
    {
        // Key lookup should be case-insensitive, because most key/value collections in 
        // .NET Framework config sections are case-insensitive.
        return "Value for given key, or null.";
    }

    public override ICollection<KeyValuePair<string, string>> GetAllValues(string prefix)
    {
        // Populate the return collection.
        return new Dictionary<string, string>() { { "one", "1" }, { "two", "2" } };
    }
}

KeyValueConfigBuilder 类提供键/值配置生成器的大部分工作和一致行为。

其他资源