Construtores de configuração para ASP.NET

Por Stephen Molloy e Rick Anderson

Os criadores de configuração fornecem um mecanismo moderno e ágil para que ASP.NET aplicativos obtenham valores de configuração de fontes externas.

Construtores de configuração:

  • Estão disponíveis no .NET Framework 4.7.1 e posterior.
  • Forneça um mecanismo flexível para ler valores de configuração.
  • Atenda a algumas das necessidades básicas dos aplicativos à medida que eles se movem para um ambiente focado em contêiner e nuvem.
  • Pode ser usado para melhorar a proteção de dados de configuração extraindo de fontes anteriormente indisponíveis (por exemplo, Azure Key Vault e variáveis de ambiente) no sistema de configuração do .NET.

Construtores de configuração chave/valor

Um cenário comum que pode ser tratado por construtores de configuração é fornecer um mecanismo básico de substituição de chave/valor para seções de configuração que seguem um padrão de chave/valor. O conceito do .NET Framework de ConfigurationBuilders não se limita a seções ou padrões de configuração específicos. No entanto, muitos dos construtores de configuração em Microsoft.Configuration.ConfigurationBuilders (github, NuGet) funcionam dentro do padrão de chave/valor.

Configurações dos construtores de configuração de chave/valor

As configurações a seguir se aplicam a todos os construtores de configuração de chave/valor no Microsoft.Configuration.ConfigurationBuilders.

Modo

Os construtores de configuração usam uma fonte externa de informações de chave/valor para preencher os elementos de chave/valor selecionados do sistema de configuração. Especificamente, as seções <appSettings/> e <connectionStrings/> recebem tratamento especial dos construtores de configuração. Os construtores funcionam em três modos:

  • Strict - O modo padrão. Nesse modo, o construtor de configuração opera apenas em seções de configuração centradas em chave/valor conhecidas. Strict mode enumera cada chave na seção. Se uma chave correspondente for encontrada na fonte externa:

    • Os construtores de configuração substituem o valor na seção de configuração resultante pelo valor da fonte externa.
  • Greedy - Este modo está intimamente relacionado ao Strict modo. Em vez de se limitar a chaves que já existem na configuração original:

    • Os construtores de configuração adicionam todos os pares de chave/valor da fonte externa na seção de configuração resultante.
  • Expand - Opera no XML bruto antes de ser analisado em um objeto de seção de configuração. Pode-se considerar como uma expansão de tokens em uma string. Qualquer parte da cadeia de caracteres XML bruta que corresponda ao padrão ${token} é candidata à expansão do token. Se nenhum valor correspondente for encontrado na fonte externa, o token não será alterado. Os construtores neste modo não estão limitados às seções <appSettings/> e <connectionStrings/>.

A seguinte marcação de web.config habilita o EnvironmentConfigBuilder em modo Strict:

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

O código a seguir lê o <appSettings/> e <connectionStrings/> mostrado no arquivo web.config anterior:

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

O código anterior definirá os valores de propriedade como:

  • Os valores no arquivo web.config se as chaves não estiverem definidas em variáveis de ambiente.
  • Os valores da variável de ambiente, se definidos.

Por exemplo, ServiceID conterá:

  • Valor do ServiceID do web.config, se a variável de ambiente ServiceID não estiver definida.
  • O valor da variável de ambiente ServiceID, se estiver definida.

A imagem a seguir mostra as <appSettings/> chaves/valores do arquivo web.config anterior definido no editor de ambiente.

A captura de tela mostra o editor de Variáveis Ambientais com as variáveis ServiceID e ServiceKey realçadas.

Observação: talvez seja necessário sair e reiniciar o Visual Studio para ver as alterações nas variáveis de ambiente.

Tratamento de prefixo

Os prefixos de chave podem simplificar a configuração de chaves porque:

  • A configuração do .NET Framework é complexa e hierárquica.
  • As fontes externas de chave/valor são geralmente básicas e planas por natureza. Por exemplo, as variáveis de ambiente não são aninhadas.

Use qualquer uma das seguintes abordagens para injetar ambos <appSettings/> e <connectionStrings/> na configuração por meio de variáveis de ambiente.

  • Com o EnvironmentConfigBuilder no modo padrão Strict e os nomes de chave apropriados no arquivo de configuração. O código e a marcação anteriores usam essa abordagem. Usando essa abordagem, você não pode ter chaves com nomes idênticos em <appSettings/> e <connectionStrings/>.
  • Use dois EnvironmentConfigBuilders em modo Greedy com prefixos distintos e stripPrefix. Com essa abordagem, o aplicativo pode ler <appSettings/> e <connectionStrings/> sem precisar atualizar o arquivo de configuração. A próxima seção, stripPrefix, mostra como fazer isso.
  • Use dois EnvironmentConfigBuilders no modo Greedy com prefixos distintos. Com essa abordagem, você não pode ter nomes de chave duplicados, pois os nomes de chave devem ser diferentes por prefixo. Por exemplo:
<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>

Com a marcação anterior, a mesma fonte de chave/valor simples pode ser usada para preencher a configuração de duas seções diferentes.

A imagem a seguir mostra as <appSettings/> e <connectionStrings/> chaves/valores do arquivo web.config anterior, definido no editor de ambiente:

A captura de tela mostra o editor de variáveis ambientais com as variáveis AppSetting_default, AppSetting_ServiceID e ConnStr_default realçadas.

O código a seguir lê as chaves/valores <appSettings/> e <connectionStrings/> contidos no arquivo web.config anterior:

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

O código anterior definirá os valores de propriedade como:

  • Os valores no arquivo web.config se as chaves não estiverem definidas em variáveis de ambiente.
  • Os valores da variável de ambiente, se definidos.

Por exemplo, usando o arquivo web.config anterior, as chaves/valores na imagem anterior do editor de ambiente e o código anterior, os seguintes valores são definidos:

Chave Valor
AppSetting_ServiceID AppSetting_ServiceID de variáveis env
AppSetting_default AppSetting_default valor padrão do ambiente
ConnStr_default ConnStr_default val de env

stripPrefix

stripPrefix: booleano, o padrão é false.

A marcação XML anterior separa as configurações do aplicativo das cadeias de conexão, mas requer que todas as chaves no arquivo web.config usem o prefixo especificado. Por exemplo, o prefixo AppSetting deve ser adicionado à chave ServiceID ("AppSetting_ServiceID"). Com stripPrefix, o prefixo não é usado no arquivo web.config . O prefixo é necessário na origem do builder de configuração (por exemplo, no ambiente). Prevemos que a maioria dos desenvolvedores usará stripPrefix.

Os aplicativos normalmente removem o prefixo. O web.config a seguir remove o prefixo:

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

No arquivo web.config anterior, a chave default está presente tanto em <appSettings/> quanto em <connectionStrings/>.

A imagem a seguir mostra as <appSettings/> e <connectionStrings/> chaves/valores do arquivo web.config anterior, definido no editor de ambiente.

A captura de tela mostra o editor de variáveis ambientais com as variáveis AppSetting_default, AppSetting_ServiceID e ConnStr_default realçadas.

O código a seguir lê os <appSettings/> e <connectionStrings/> chaves/valores contidos no arquivo web.config anterior:

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

O código anterior definirá os valores de propriedade como:

  • Os valores no arquivo web.config se as chaves não estiverem definidas em variáveis de ambiente.
  • Os valores da variável de ambiente, se definidos.

Por exemplo, usando o arquivo web.config anterior, as chaves/valores na imagem anterior do editor de ambiente e o código anterior, os seguintes valores são definidos:

Chave Valor
ID de Serviço AppSetting_ServiceID das variáveis de ambiente
padrão AppSetting_valor padrão do env
padrão ConnStr_default val de env

tokenPadrão

tokenPattern: String, o padrão é @"\$\{(\w+)\}"

O Expand comportamento dos construtores pesquisa o XML bruto em busca de tokens que se pareçam com ${token}. A pesquisa é feita com a expressão regular padrão @"\$\{(\w+)\}". O conjunto de caracteres que corresponde a \w é mais restrito do que o XML e muitas fontes de configuração permitem. Use tokenPattern quando mais caracteres do que @"\$\{(\w+)\}" forem necessários no nome do token.

tokenPattern: String:

  • Permite que os desenvolvedores alterem o regex usado para correspondência de token.
  • Nenhuma validação é feita para garantir que seja um regex bem formado e não perigoso.
  • Ele deve conter um grupo de captura. Todo o regex deve corresponder ao token inteiro. A primeira captura deve ser o nome do token a ser pesquisado na fonte de configuração.

Construtores de configuração em Microsoft.Configuration.ConfigurationBuilders

Construtor de configuração de ambiente

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

O EnvironmentConfigBuilder:

  • É o mais simples dos construtores de configuração.
  • Lê valores do ambiente.
  • Não tem nenhuma opção de configuração adicional.
  • O name valor do atributo é arbitrário.

Nota: Em um ambiente de contêiner do Windows, as variáveis definidas no tempo de execução são injetadas apenas no ambiente de processo EntryPoint. Os aplicativos que são executados como um serviço ou como um processo que não faz parte do EntryPoint não capturam essas variáveis, a menos que sejam injetadas por meio de um mecanismo no contêiner. Para contêineres do IIS/baseados em ASP.NET, a versão atual do ServiceMonitor.exe lida com isso somente no DefaultAppPool. Outras variantes de contêiner baseadas em Windows podem precisar desenvolver seu próprio mecanismo de injeção para processos que não são do EntryPoint.

UserSecretsConfigBuilder

Aviso

Nunca armazene senhas, cadeias de conexão confidenciais ou outros dados confidenciais no código-fonte. Os segredos de produção não devem ser usados para desenvolvimento ou teste.

<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" />

No XML anterior, o caminho userSecretsFile pode usar ~/ ou ~\. Por exemplo, o caminho pode ser escrito como userSecretsFile="~/secrets.file. Consulte a classe ConfigurationBuilders Utils para obter mais informações.

Esse construtor de configuração fornece um recurso semelhante ao ASP.NET Core Secret Manager.

O UserSecretsConfigBuilder pode ser usado em projetos do .NET Framework, mas um arquivo de segredos deve ser especificado. Como alternativa, você pode definir a UserSecretsId propriedade no arquivo de projeto e criar o arquivo de segredos brutos no local correto para leitura. Para manter as dependências externas fora do seu projeto, o arquivo secreto é formatado em XML. A formatação XML é uma questão de implementação e não se deve confiar no formato. Se você precisar compartilhar um arquivo secrets.json com projetos do .NET Core, considere usar o SimpleJsonConfigBuilder. O SimpleJsonConfigBuilder formato do .NET Core também deve ser considerado um detalhe de implementação sujeito a alterações.

Atributos de configuração para UserSecretsConfigBuilder:

  • userSecretsId - Este é o método preferencial para identificar um arquivo de segredos XML. Ele funciona de forma semelhante ao .NET Core, que usa uma propriedade de UserSecretsId projeto para armazenar esse identificador. A string deve ser única, não precisa ser um GUID. Com esse atributo, UserSecretsConfigBuilder procura em um local conhecido (%APPDATA%\Microsoft\UserSecrets\<UserSecrets Id>\secrets.xml) por um arquivo de segredos associado a este identificador.
  • userSecretsFile - Um atributo opcional que especifica o arquivo que contém os segredos. O ~ caractere pode ser usado no início para fazer referência à raiz do aplicativo. Esse atributo ou o userSecretsId atributo é necessário. Se ambos forem especificados, userSecretsFile terá precedência.
  • optional: booleano, valor true padrão - Impede uma exceção se o arquivo de segredos não puder ser encontrado.
  • O name valor do atributo é arbitrário.

O arquivo de segredos tem o seguinte formato:

<?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" />

O AzureKeyVaultConfigBuilder lê valores armazenados no Azure Key Vault.

vaultName é necessário (o nome do vault ou um URI para o vault). Os outros atributos permitem o controle de qual vault se conectar, mas só são necessários se o aplicativo não estiver sendo executado em um ambiente que funcione com Microsoft.Azure.Services.AppAuthentication. A biblioteca de Autenticação de Serviços do Azure é usada para coletar automaticamente informações de conexão do ambiente de execução, se possível. Você pode substituir a coleta automática de informações de conexão fornecendo uma cadeia de conexão.

  • vaultName - Obrigatório se uri não for fornecido. Especifica o nome do cofre em sua assinatura do Azure de onde são lidos os pares chave/valor.
  • uri - Conecta-se a outros provedores do Key Vault com o valor uri especificado. Se não for especificado, Azure (vaultName) será usado como o provedor do repositório.
  • version - O Azure Key Vault fornece um recurso de controle de versão para segredos. Se version for especificado, o builder recuperará apenas os segredos que correspondem a essa versão.
  • preloadSecretNames- Por padrão, esse construtor consulta todos os nomes de chave no cofre de chaves quando ele é inicializado. Para impedir a leitura de todos os valores de chave, defina esse atributo como false. Configurar isto para false permite ler os segredos um de cada vez. Ler segredos um de cada vez pode ser útil se o cofre permitir acesso "Obter", mas não acesso "Lista". Nota: Ao usar Greedy o modo, preloadSecretNames deve ser true (o padrão).

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 é um construtor de configuração básica que usa os arquivos de um diretório como fonte de valores. O nome de um arquivo é a chave e o conteúdo é o valor. Esse construtor de configuração pode ser útil ao ser executado em um ambiente de contêiner orquestrado. Sistemas como o Docker Swarm e o Kubernetes fornecem secrets aos contêineres Windows orquestrados nessa maneira de chave por arquivo.

Detalhes do atributo:

  • directoryPath - Obrigatório. Especifica um caminho para procurar valores. Os segredos do Docker para Windows são armazenados no diretório C:\ProgramData\Docker\secrets por padrão.
  • ignorePrefix - Os arquivos que começam com esse prefixo são excluídos. O padrão é "ignorar".
  • keyDelimiter - O valor padrão é null. Se especificado, o construtor de configuração percorre vários níveis do diretório, criando nomes de chave com esse delimitador. Se esse valor for null, o construtor de configuração examinará apenas o nível superior do diretório.
  • optional - O valor padrão é false. Especifica se o construtor de configuração deve causar erros se o diretório de origem não existir.

Construtor de Configuração Json Simples

Aviso

Nunca armazene senhas, cadeias de conexão confidenciais ou outros dados confidenciais no código-fonte. Os segredos de produção não devem ser usados para desenvolvimento ou teste.

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

Os projetos do .NET Core frequentemente usam arquivos JSON para configuração. O construtor SimpleJsonConfigBuilder permite que arquivos JSON do .NET Core sejam usados no .NET Framework. Esse construtor de configuração fornece um mapeamento básico de uma fonte de chave/valor simples para áreas específicas de chave/valor da configuração do .NET Framework. Esse construtor de configuração não fornece configurações hierárquicas. O arquivo de backup JSON é semelhante a um dicionário, não a um objeto hierárquico complexo. Um arquivo hierárquico de vários níveis pode ser usado. Esse provedor flatten a profundidade anexando o nome da propriedade em cada nível usando : como um delimitador.

Detalhes do atributo:

  • jsonFile - Obrigatório. Especifica o arquivo JSON do qual ler. O ~ caractere pode ser usado no início para fazer referência à raiz do aplicativo.

  • optional - Booleano, o valor padrão é true. Impede o lançamento de exceções se o arquivo JSON não puder ser encontrado.

  • jsonMode - [Flat|Sectional]. Flat é o padrão. Quando jsonMode é Flat, o arquivo JSON é uma única fonte de chave/valor simples. Os EnvironmentConfigBuilder e AzureKeyVaultConfigBuilder também são fontes únicas de chave/valor simples. Quando o SimpleJsonConfigBuilder está configurado no modo Sectional:

    • O arquivo JSON é conceitualmente dividido apenas no nível superior em vários dicionários.
    • Cada um dos dicionários é aplicado apenas à seção de configuração que corresponde ao nome da propriedade de nível superior associado a eles. Por exemplo:
    {
        "appSettings" : {
            "setting1" : "value1",
            "setting2" : "value2",
            "complex" : {
                "setting1" : "complex:value1",
                "setting2" : "complex:value2",
            }
        }
    }

Ordem dos construtores de configuração

Consulte Ordem de execução de ConfigurationBuilders no repositório GitHub aspnet/MicrosoftConfigurationBuilders .

Implementando um construtor de configuração de chave/valor personalizado

Se os criadores de configuração não atenderem às suas necessidades, você poderá escrever um personalizado. A KeyValueConfigBuilder classe base lida com modos de substituição e a maioria das preocupações de prefixo. Um projeto de execução precisa apenas:

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

A KeyValueConfigBuilder classe base fornece grande parte do trabalho e do comportamento consistente entre os construtores de configuração de chave/valor.

Recursos adicionais