Variáveis de ambiente e appsettings no .NET | Dia a Dia | CSharp
- Variáveis de ambiente e appsettings no .NET | Dia a Dia | CSharp
Visão geral
No .NET, principalmente em ASP.NET Core, a configuração da aplicação gira em torno de um conceito central: IConfiguration.
A ideia é simples:
- a aplicação pode ler configuração de várias fontes
- essas fontes são combinadas
- a última fonte carregada normalmente sobrescreve a anterior
- você acessa tudo por uma API única
O próprio ecossistema ASP.NET Core trata configuração como algo unificado, permitindo combinar JSON, variáveis de ambiente, argumentos de linha de comando e outros providers em uma única árvore de configuração .
Na prática, isso resolve um problema importante:
- você usa
appsettings.jsonpara valores comuns - usa
appsettings.Development.jsonpara ambiente local - usa variáveis de ambiente para produção e segredos
- o código continua lendo do mesmo lugar
O que é IConfiguration
IConfiguration é a abstração usada pelo .NET para acessar configurações da aplicação. Ela permite ler dados vindos de múltiplas fontes, como:
appsettings.jsonappsettings.{Environment}.json- variáveis de ambiente
- argumentos de linha de comando
- user secrets
- providers customizados
Essa é exatamente a proposta descrita no material de ASP.NET Core: unificar o acesso a diferentes fontes de configuração, incluindo arquivos JSON, variáveis de ambiente e argumentos .
Exemplo simples:
string? connectionString = builder.Configuration["ConnectionStrings:DefaultConnection"];
string? nomeApi = builder.Configuration["MinhaApi:Nome"];
Repare no padrão:
:separa níveisMinhaApi:Nomeequivale a um objeto JSON com propriedade interna
Estrutura mental correta
Pense assim:
IConfiguration= mecanismo bruto de leituraappsettings.json= fonte de configuração- variáveis de ambiente = outra fonte de configuração
Options Pattern= forma recomendada de mapear configuração para classes fortemente tipadas
Ou seja:
- para algo rápido, você pode usar
builder.Configuration["Chave"] - para algo sério, repetido e organizado, prefira
Options
Fontes mais comuns de configuração
appsettings.json
É o arquivo base da aplicação.
Exemplo:
{
"MinhaApi": {
"Nome": "Comandos API",
"TimeoutSegundos": 30,
"AtivarCache": true
},
"ConnectionStrings": {
"DefaultConnection": "Server=localhost;Database=MinhaBase;User Id=sa;Password=123456;"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}
Esse é o lugar ideal para:
- valores padrão
- configurações não sensíveis
- estrutura base da aplicação
appsettings.Development.json
É o arquivo específico para desenvolvimento local.
Exemplo:
{
"MinhaApi": {
"Nome": "Comandos API - Dev",
"TimeoutSegundos": 10
},
"ConnectionStrings": {
"DefaultConnection": "Server=localhost;Database=MinhaBaseDev;User Id=sa;Password=123456;"
}
}
Esse arquivo sobrescreve o appsettings.json quando o ambiente ativo é Development.
Variáveis de ambiente
São muito usadas em produção.
Exemplo de chave:
ConnectionStrings__DefaultConnection
MinhaApi__Nome
MinhaApi__TimeoutSegundos
No ambiente, você usa __ no lugar de :.
Então:
MinhaApi:Nomeno códigoMinhaApi__Nomena variável de ambiente
Isso é padrão prático do ecossistema .NET para representar seções hierárquicas via environment variables.
User Secrets
Muito útil em desenvolvimento local para segredos.
Exemplos:
- senha de banco local
- token de API
- client secret OAuth
- chave JWT local
Use isso em vez de deixar segredos no appsettings.Development.json.
Linha de comando
Também pode sobrescrever config:
dotnet run --MinhaApi:Nome="API via CLI"
É menos comum no dia a dia de API web, mas existe.
Ordem de precedência
O ponto mais importante para quem está começando é entender:
não basta ter várias fontes; a ordem importa.
Regra prática:
- valores carregados depois sobrescrevem os anteriores
No fluxo padrão de uma aplicação ASP.NET Core moderna, a ideia usual é:
appsettings.jsonappsettings.{Environment}.json- user secrets em desenvolvimento
- variáveis de ambiente
- linha de comando
Na prática, isso significa:
- um valor no JSON pode ser substituído por outro no JSON do ambiente
- esse valor pode ser substituído por uma variável de ambiente em produção
Esse modelo de múltiplos providers em camadas é exatamente a base descrita pelo capítulo de configurações do ASP.NET Core .
Ambiente no .NET: Development, Staging, Production
O .NET trabalha fortemente com o conceito de ambiente.
Os nomes mais comuns são:
DevelopmentStagingProduction
Você descobre o ambiente atual assim:
if (builder.Environment.IsDevelopment())
{
Console.WriteLine("Ambiente de desenvolvimento");
}
if (builder.Environment.IsProduction())
{
Console.WriteLine("Ambiente de produção");
}
Também existe:
string environmentName = builder.Environment.EnvironmentName;
Exemplo:
Console.WriteLine(builder.Environment.EnvironmentName);
Como definir o ambiente
A variável mais comum é:
ASPNETCORE_ENVIRONMENT
Também existe:
DOTNET_ENVIRONMENT
Em aplicações web, ASPNETCORE_ENVIRONMENT é a mais vista no dia a dia.
Exemplo no Windows PowerShell:
$env:ASPNETCORE_ENVIRONMENT="Development"
dotnet run
Exemplo no Linux:
export ASPNETCORE_ENVIRONMENT=Development
dotnet run
Exemplo em Docker Compose:
environment:
ASPNETCORE_ENVIRONMENT: Production
Como isso fica em desenvolvimento
Cenário típico:
ASPNETCORE_ENVIRONMENT=Development- arquivo
appsettings.json - arquivo
appsettings.Development.json - user secrets para segredos
Exemplo de appsettings.json
{
"MinhaApi": {
"Nome": "Minha API",
"TimeoutSegundos": 30
},
"ConnectionStrings": {
"DefaultConnection": ""
}
}
Exemplo de appsettings.Development.json
{
"MinhaApi": {
"Nome": "Minha API DEV",
"TimeoutSegundos": 5
}
}
Exemplo de User Secrets
{
"ConnectionStrings": {
"DefaultConnection": "Server=localhost;Database=MinhaBaseDev;User Id=sa;Password=SenhaForteAqui;"
}
}
Resultado final em development
A aplicação enxergará algo como:
MinhaApi:Nome = "Minha API DEV"MinhaApi:TimeoutSegundos = 5ConnectionStrings:DefaultConnection = valor do User Secrets
Como isso fica em produção
Em produção, o padrão saudável é:
ASPNETCORE_ENVIRONMENT=Productionappsettings.jsoncom defaults seguros- segredos e diferenças do ambiente via variáveis de ambiente ou secret manager externo
- nunca deixar senha real em arquivo commitado
Exemplo de appsettings.json
{
"MinhaApi": {
"Nome": "Minha API",
"TimeoutSegundos": 30
},
"ConnectionStrings": {
"DefaultConnection": ""
}
}
Variáveis de ambiente no servidor
ASPNETCORE_ENVIRONMENT=Production
MinhaApi__TimeoutSegundos=60
ConnectionStrings__DefaultConnection=Server=sql-prod;Database=MinhaBase;User Id=app;Password=SenhaSuperForte
Resultado final em produção
A aplicação enxergará:
MinhaApi:Nome = "Minha API"MinhaApi:TimeoutSegundos = 60ConnectionStrings:DefaultConnection = valor da variável de ambiente
Regra prática de ouro
Use assim:
appsettings.jsonpara base comumappsettings.Development.jsonpara ajustes locais- User Secrets para segredos em dev
- variáveis de ambiente para produção
- Azure Key Vault, AWS Secrets Manager, Doppler, Vault ou similar para ambientes mais maduros
Exemplo real de leitura com builder.Configuration
Program.cs
var builder = WebApplication.CreateBuilder(args);
string? nomeApi = builder.Configuration["MinhaApi:Nome"];
string? timeout = builder.Configuration["MinhaApi:TimeoutSegundos"];
string? connectionString = builder.Configuration["ConnectionStrings:DefaultConnection"];
Console.WriteLine($"Nome API: {nomeApi}");
Console.WriteLine($"Timeout: {timeout}");
Console.WriteLine($"ConnectionString: {connectionString}");
var app = builder.Build();
app.MapGet("/", () => new
{
Nome = builder.Configuration["MinhaApi:Nome"],
Timeout = builder.Configuration["MinhaApi:TimeoutSegundos"]
});
app.Run();
Isso funciona, mas não é a abordagem mais organizada para configurações maiores.
Padrão recomendado: Options Pattern
O próprio material de ASP.NET Core trata o Options Pattern como parte central da gestão de settings .
Esse é o padrão mais limpo.
appsettings.json
{
"MinhaApi": {
"Nome": "Minha API",
"TimeoutSegundos": 30,
"AtivarCache": true
}
}
Classe de configuração
public sealed class MinhaApiOptions
{
public const string SectionName = "MinhaApi";
public string Nome { get; set; } = string.Empty;
public int TimeoutSegundos { get; set; }
public bool AtivarCache { get; set; }
}
Registro no Program.cs
var builder = WebApplication.CreateBuilder(args);
builder.Services
.AddOptions<MinhaApiOptions>()
.Bind(builder.Configuration.GetSection(MinhaApiOptions.SectionName))
.ValidateDataAnnotations()
.ValidateOnStart();
var app = builder.Build();
app.MapGet("/", (Microsoft.Extensions.Options.IOptions<MinhaApiOptions> options) =>
{
var config = options.Value;
return Results.Ok(new
{
config.Nome,
config.TimeoutSegundos,
config.AtivarCache
});
});
app.Run();
Melhorando com validação
Para evitar subir a aplicação com config inválida, valide.
Classe com validação
using System.ComponentModel.DataAnnotations;
public sealed class MinhaApiOptions
{
public const string SectionName = "MinhaApi";
[Required]
public string Nome { get; set; } = string.Empty;
[Range(1, 300)]
public int TimeoutSegundos { get; set; }
public bool AtivarCache { get; set; }
}
Se faltar Nome ou TimeoutSegundos estiver fora do intervalo, a aplicação já falha na inicialização por causa de ValidateOnStart().
Isso é excelente para produção.
Quando usar IOptions, IOptionsSnapshot e IOptionsMonitor
IOptions<T>
Use quando:
- a config não muda durante a execução
- leitura simples basta
app.MapGet("/config", (Microsoft.Extensions.Options.IOptions<MinhaApiOptions> options) =>
{
return options.Value;
});
IOptionsSnapshot<T>
Use quando:
- você quer pegar configuração por request
- cenário comum em aplicações web
- útil quando JSON pode ser recarregado
Exemplo em serviço scoped ou controller.
IOptionsMonitor<T>
Use quando:
- você precisa reagir a mudanças de configuração em tempo de execução
- singleton precisa acessar valores atualizados
Como o JSON é mapeado para classes
JSON
{
"Email": {
"Host": "smtp.exemplo.com",
"Porta": 587,
"Usuario": "sistema",
"Senha": "abc123"
}
}
Classe
public sealed class EmailOptions
{
public const string SectionName = "Email";
public string Host { get; set; } = string.Empty;
public int Porta { get; set; }
public string Usuario { get; set; } = string.Empty;
public string Senha { get; set; } = string.Empty;
}
Registro
builder.Services
.AddOptions<EmailOptions>()
.Bind(builder.Configuration.GetSection(EmailOptions.SectionName))
.ValidateOnStart();
Como obter valores diretamente em classes e serviços
Jeito rápido com IConfiguration
public sealed class EmailService
{
private readonly IConfiguration _configuration;
public EmailService(IConfiguration configuration)
{
_configuration = configuration;
}
public void ExibirConfig()
{
string? host = _configuration["Email:Host"];
string? porta = _configuration["Email:Porta"];
Console.WriteLine($"{host}:{porta}");
}
}
Funciona, mas espalha strings mágicas.
Jeito melhor com IOptions
using Microsoft.Extensions.Options;
public sealed class EmailService
{
private readonly EmailOptions _options;
public EmailService(IOptions<EmailOptions> options)
{
_options = options.Value;
}
public void ExibirConfig()
{
Console.WriteLine($"{_options.Host}:{_options.Porta}");
}
}
Melhor porque:
- é tipado
- facilita manutenção
- evita typo em chave
- centraliza a estrutura da config
Exemplo completo prático
appsettings.json
{
"MinhaApi": {
"Nome": "API de Pedidos",
"TimeoutSegundos": 30,
"AtivarCache": true
},
"ConnectionStrings": {
"DefaultConnection": "Server=localhost;Database=Pedidos;User Id=sa;Password=123456;"
}
}
appsettings.Development.json
{
"MinhaApi": {
"Nome": "API de Pedidos DEV",
"TimeoutSegundos": 10
}
}
Classe MinhaApiOptions.cs
using System.ComponentModel.DataAnnotations;
public sealed class MinhaApiOptions
{
public const string SectionName = "MinhaApi";
[Required]
public string Nome { get; set; } = string.Empty;
[Range(1, 300)]
public int TimeoutSegundos { get; set; }
public bool AtivarCache { get; set; }
}
Program.cs
using Microsoft.Extensions.Options;
var builder = WebApplication.CreateBuilder(args);
builder.Services
.AddOptions<MinhaApiOptions>()
.Bind(builder.Configuration.GetSection(MinhaApiOptions.SectionName))
.ValidateDataAnnotations()
.ValidateOnStart();
var app = builder.Build();
app.MapGet("/config-bruta", (IConfiguration configuration) =>
{
return Results.Ok(new
{
Nome = configuration["MinhaApi:Nome"],
Timeout = configuration["MinhaApi:TimeoutSegundos"],
AtivarCache = configuration["MinhaApi:AtivarCache"],
Connection = configuration["ConnectionStrings:DefaultConnection"]
});
});
app.MapGet("/config-tipada", (IOptions<MinhaApiOptions> options) =>
{
return Results.Ok(options.Value);
});
app.MapGet("/ambiente", (IHostEnvironment env) =>
{
return Results.Ok(new
{
env.EnvironmentName,
Development = env.IsDevelopment(),
Production = env.IsProduction()
});
});
app.Run();
Como o Program.cs normalmente já vem configurado
Nas versões modernas do ASP.NET Core, o builder padrão já trabalha com configuração integrada. O framework foi desenhado justamente para combinar providers e expor tudo via builder.Configuration .
Ou seja, na maioria dos casos você não precisa sair adicionando manualmente AddJsonFile, AddEnvironmentVariables e similares para o básico.
O comum é só usar:
var builder = WebApplication.CreateBuilder(args);
e depois acessar:
builder.Configuration
Quando você precisa configurar providers manualmente
Quando quer controle maior.
Exemplo:
var builder = WebApplication.CreateBuilder(args);
builder.Configuration.Sources.Clear();
builder.Configuration
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{builder.Environment.EnvironmentName}.json", optional: true, reloadOnChange: true)
.AddEnvironmentVariables()
.AddCommandLine(args);
Use isso quando:
- quer limpar providers padrão
- quer controlar ordem explicitamente
- quer adicionar provider customizado
- quer comportamento muito específico
Para iniciante, isso não deve ser o padrão.
reloadOnChange
Quando um appsettings.json está com reloadOnChange: true, o .NET pode recarregar valores quando o arquivo muda.
Exemplo manual:
builder.Configuration.AddJsonFile(
"appsettings.json",
optional: false,
reloadOnChange: true);
Isso é útil em desenvolvimento e alguns cenários controlados.
Em produção, normalmente você depende mais de variáveis de ambiente ou secret stores do que de editar arquivo no servidor.
Como ficam variáveis hierárquicas
JSON
{
"RabbitMq": {
"Host": "localhost",
"Port": 5672,
"Username": "guest",
"Password": "guest"
}
}
Variáveis de ambiente equivalentes
RabbitMq__Host=localhost
RabbitMq__Port=5672
RabbitMq__Username=guest
RabbitMq__Password=guest
Leitura no código
string? host = builder.Configuration["RabbitMq:Host"];
Connection strings
Existe um padrão muito comum no .NET:
JSON
{
"ConnectionStrings": {
"DefaultConnection": "Server=localhost;Database=MinhaBase;User Id=sa;Password=123456;"
}
}
Leitura correta
string? connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
Isso é melhor do que usar a chave direta manualmente.
Exemplo com banco
string connectionString = builder.Configuration.GetConnectionString("DefaultConnection")
?? throw new InvalidOperationException("Connection string 'DefaultConnection' não encontrada.");
Segredos: o que nunca fazer
Nunca faça isso em projeto real:
{
"Jwt": {
"Key": "minha-chave-secreta-de-producao"
}
}
ou:
var senha = "SenhaSuperSecreta123";
Segredos não devem ficar:
- no código-fonte
- em arquivo commitado
- em print
- em documentação pública
O próprio material do capítulo de settings destaca a necessidade de não manter parâmetros sensíveis versionados no controle de código-fonte .
O que usar para segredos
Em development
- User Secrets
Em produção
- variáveis de ambiente
- Azure Key Vault
- AWS Secrets Manager
- HashiCorp Vault
- secret do Kubernetes
- recurso equivalente da sua plataforma
Exemplo de User Secrets
Inicializar:
dotnet user-secrets init
Salvar segredo:
dotnet user-secrets set "ConnectionStrings:DefaultConnection" "Server=localhost;Database=MinhaBaseDev;User Id=sa;Password=SenhaSecreta;"
Ler no código:
string? connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
Como saber de onde veio um valor
Isso confunde muito no começo.
Exemplo:
- você olha o
appsettings.json - altera algo
- a aplicação continua com outro valor
- o motivo pode ser uma variável de ambiente sobrescrevendo
Uma forma simples de investigar:
foreach (var item in builder.Configuration.AsEnumerable())
{
Console.WriteLine($"{item.Key} = {item.Value}");
}
Isso ajuda a enxergar a configuração final resolvida.
Configuração de logging também usa esse sistema
O sistema de logging em ASP.NET Core também pode ser configurado por configuração. Exemplo de appsettings.json:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
},
"Console": {
"IncludeScopes": true
}
}
}
O uso de Logging:LogLevel e Console:IncludeScopes em appsettings.json aparece de forma prática no material de observabilidade em .NET .
Isso mostra algo importante:
- configuração da aplicação
- configuração de logging
- configuração de providers
Tudo segue a mesma infraestrutura de config.
Exemplo prático de Development e Production no pipeline
É muito comum fazer isso:
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.Run();
Esse padrão de tratamento diferente entre desenvolvimento e produção aparece claramente em exemplos de ASP.NET Core 9 .
Na prática:
Development
- mais diagnóstico
- erros detalhados
- ferramentas de dev
- configurações locais
Production
- erro genérico
- HSTS
- segredos externos
- foco em segurança e estabilidade
Como eu recomendo organizar um projeto
Estrutura de arquivos
appsettings.json
appsettings.Development.json
Program.cs
Configurations/
MinhaApiOptions.cs
RabbitMqOptions.cs
JwtOptions.cs
Regras
- tudo que for config estruturada vai para classe
Options - nome da seção igual ao nome lógico da config
- segredos fora de arquivos commitados
ValidateOnStart()para config críticaGetConnectionString()para connection strings- nada de sair lendo chave crua em qualquer lugar
Exemplo de organização limpa
JwtOptions.cs
using System.ComponentModel.DataAnnotations;
public sealed class JwtOptions
{
public const string SectionName = "Jwt";
[Required]
public string Issuer { get; set; } = string.Empty;
[Required]
public string Audience { get; set; } = string.Empty;
[Required]
public string Key { get; set; } = string.Empty;
[Range(1, 1440)]
public int ExpirationMinutes { get; set; }
}
Program.cs
builder.Services
.AddOptions<JwtOptions>()
.Bind(builder.Configuration.GetSection(JwtOptions.SectionName))
.ValidateDataAnnotations()
.ValidateOnStart();
appsettings.json
{
"Jwt": {
"Issuer": "MinhaApi",
"Audience": "MinhaApi.Clientes",
"Key": "",
"ExpirationMinutes": 60
}
}
Produção
Jwt__Key=chave-real-da-producao
Erros comuns de iniciante
1. Colocar segredo em appsettings.json
Errado.
2. Ler tudo com string mágica
Exemplo ruim:
var valor = configuration["MinhaCoisa:MeuCampo:OutroCampo"];
Em excesso, isso vira bagunça.
3. Duplicar config em muitos lugares
A config deve ter um ponto central.
4. Não validar na inicialização
A aplicação sobe e quebra só quando usa a config.
5. Misturar config de ambiente com código
Errado:
if (builder.Environment.IsProduction())
{
string connection = "Server=sql-prod;...";
}
else
{
string connection = "Server=localhost;...";
}
Correto: deixar isso na configuração.
6. Commitar appsettings.Production.json com valores reais
Evite.
7. Esquecer que variável de ambiente sobrescreve JSON
Esse é clássico.
Padrão prático que eu recomendo para você
Para projeto .NET comum:
Desenvolvimento
appsettings.jsonappsettings.Development.json- User Secrets para segredo
ASPNETCORE_ENVIRONMENT=Development
Produção
appsettings.jsonsó com defaults segurosASPNETCORE_ENVIRONMENT=Production- segredos e diferenças via variáveis de ambiente ou cofre de segredos
Código
IOptions<T>para configurações estruturadasGetConnectionString()para bancoIsDevelopment()eIsProduction()para pipeline e comportamentoValidateOnStart()para config crítica
Exemplo final completo e saudável
appsettings.json
{
"MinhaApi": {
"Nome": "API Orquestradora",
"TimeoutSegundos": 30
},
"ConnectionStrings": {
"DefaultConnection": ""
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}
appsettings.Development.json
{
"MinhaApi": {
"Nome": "API Orquestradora DEV",
"TimeoutSegundos": 10
}
}
MinhaApiOptions.cs
using System.ComponentModel.DataAnnotations;
public sealed class MinhaApiOptions
{
public const string SectionName = "MinhaApi";
[Required]
public string Nome { get; set; } = string.Empty;
[Range(1, 300)]
public int TimeoutSegundos { get; set; }
}
Program.cs
using Microsoft.Extensions.Options;
var builder = WebApplication.CreateBuilder(args);
builder.Services
.AddOptions<MinhaApiOptions>()
.Bind(builder.Configuration.GetSection(MinhaApiOptions.SectionName))
.ValidateDataAnnotations()
.ValidateOnStart();
string connectionString = builder.Configuration.GetConnectionString("DefaultConnection")
?? throw new InvalidOperationException("Connection string 'DefaultConnection' não encontrada.");
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/error");
app.UseHsts();
}
app.MapGet("/config", (IOptions<MinhaApiOptions> options, IHostEnvironment env) =>
{
return Results.Ok(new
{
Ambiente = env.EnvironmentName,
options.Value.Nome,
options.Value.TimeoutSegundos
});
});
app.Run();
Variáveis de ambiente em produção
ASPNETCORE_ENVIRONMENT=Production
ConnectionStrings__DefaultConnection=Server=sql-prod;Database=MinhaBase;User Id=app;Password=SenhaForte
MinhaApi__TimeoutSegundos=60
Resumo operacional
Use appsettings.json para
- base da aplicação
- valores não sensíveis
- estrutura comum
Use appsettings.Development.json para
- ajustes locais de desenvolvimento
- sem segredos reais
Use User Secrets para
- segredos locais de desenvolvimento
Use variáveis de ambiente para
- produção
- container
- servidor
- CI/CD
- secrets
Use IConfiguration para
- leitura simples e pontual
Use IOptions<T> para
- configuração séria
- organização
- manutenção
- validação
Use Environment para
- comportamento diferente entre dev e prod
- pipeline HTTP
- debug e segurança
Referências dos arquivos que usei
- O capítulo Managing Application Settings de ASP.NET Core 9.0 Essentials descreve
IConfiguration, providers e o Options Pattern como base do sistema de configuração do ASP.NET Core - O mesmo material mostra o padrão de comportamento diferente entre
Developmente outros ambientes no pipeline HTTP, comUseDeveloperExceptionPage,UseExceptionHandlereUseHsts - O livro sobre observabilidade em .NET mostra uso real de
appsettings.jsonpara configurar logging, incluindoLogLeveleConsole:IncludeScopes