星期四, 5月 16, 2024

[.NET] 設定檔 - appsettings.json

在 .NET 8 上建立 Worker Service 預設有 appsettings.json 和 appsettings.Development.json 兩個 json 檔案
可以自行新增 json 檔案,在下述三篇文章內,都有提到新增 json 檔案時,可以透過調整 project 檔案,讓新增 json 檔案有巢狀階層,但實際上操作發現,只要是 appsettings 開頭,就會直接變成巢狀階層,VS 版本為 17.9.6
環境模式

在 Use multiple environments in ASP.NET Core 內提到有三種環境模式,分別為
  • Development:launchSettings.json 內 DOTNET_ENVIRONMENT 預設值
  • Staging
  • Production:沒有設定 DOTNET_ENVIRONMENT 預設值
可根據不同環境模式載入不同的 appsettings.json 檔案設定,但部屬出去後不會有 launchSettings.json,也就沒有 DOTNET_ENVIRONMENT,預設即為 Production,只會載入 appsettings.json 而已,不會載入 appsettings.Development.json,另外除了系統環境變數 (該筆記是DOTNET_ENVIRONMENT) 外,也可以自訂環境變數 (該筆記是 SecretKey)。


測試 Worker Service 專案

延續 [.NET] 設定檔 - launchSettings.json 內容,已經建立 Staging Profile,後續會記錄 profile 切換 appsettings.json、自訂環境變數和部屬為 Windows Service,相關設定檔案如下

launchSettings.json
{
  "$schema": "http://json.schemastore.org/launchsettings.json",
  "profiles": {
    "EnvironmentsSample": {
      "commandName": "Project",
      "dotnetRunMessages": true,
      "environmentVariables": {
        "DOTNET_ENVIRONMENT": "Development",
        // 自訂環境變數 SecretKey
        "SecretKey": "Secret key value From EnvironmentsSample"
      }
    },
    "Staging": {
      "commandName": "Project",
      "dotnetRunMessages": true,
      "environmentVariables": {
        "DOTNET_ENVIRONMENT": "Staging"
        // 沒有設定 SecretKey
      }
    }
  }
}
appsettings.json
{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "ConnectionStrings": {
    "SqlServer": "Server=RemoteIP;Database=AdventureWorks2022;Trusted_Connection=True;TrustServerCertificate=true"
  }
}
appsettings.Development.json
{
  "ConnectionStrings": {
    "SqlServer": "Server=.;Database=AdventureWorks2022;Trusted_Connection=True;TrustServerCertificate=true"
  }
}
appsettings.Staging.json
{
  "ConnectionStrings": {
    "SqlServer": "Server=StagingIP;Database=AdventureWorks2022;Trusted_Connection=True;TrustServerCertificate=true"
  }
}

根據 profile 取得 DOTNET_ENVIRONMENT 環境變數


取得 DOTNET_ENVIRONMENT 環境變數的三種方式
namespace EnvironmentsSample
{
    public class Program
    {
        public static void Main(string[] args)
        {
            HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);

            Console.WriteLine("----- 根據 profile 來取得 DOTNET_ENVIRONMENT 環境變數 -----");
            Console.WriteLine($"builder.Environment.EnvironmentName:{builder.Environment.EnvironmentName}");
            Console.WriteLine($"builder.Configuration[\"DOTNET_ENVIRONMENT\"]:{builder.Configuration["DOTNET_ENVIRONMENT"]}");
            Console.WriteLine($"Environment.GetEnvironmentVariable(\"DOTNET_ENVIRONMENT\"):{Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT")}");
        }
    }
}
下圖是以 EnvironmnetSample profile 的執行結果

根據 profile 的 DOTNET_ENVIRONMENT 來決定 appsettings 設定檔案


不使用 Host.CreateApplicationBuilder 產生預設 configuration,手動自行產生 configuration 來讀取
讀取 appsettings 內的連線字串
namespace EnvironmentsSample
{
    public class Program
    {
        public static void Main(string[] args)
        {
            Console.WriteLine("----- 根據 profile 的 DOTNET_ENVIRONMENT 來決定 appsettings 設定檔案 -----");
            string environment = Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT");

            IConfiguration configuration = new ConfigurationBuilder()
                .SetBasePath(AppContext.BaseDirectory)
                .AddJsonFile("appsettings.json")
                .AddJsonFile($"appsettings.{environment}.json", optional: true)
                .AddEnvironmentVariables()
                .Build();

            string connectionString = configuration.GetConnectionString("SqlServer");

            Console.WriteLine($"Environment.GetEnvironmentVariable(\"DOTNET_ENVIRONMENT\"):{environment}");
            Console.WriteLine(connectionString);
        }
    }
}
下圖是以 Staging profile 的執行結果
下述 AddJsonFile() 第二參數,在對應 profile 載入 appsettings.json 時,理論上都會設定,萬一對應 appsettings.json 不存在就不載入該設定檔案就是
.AddJsonFile($"appsettings.{environment}.json", optional: true)

自訂環境變數:SecretKey

在 Configuration providers in .NET 內有提到該段文字


在 Windows 和  launchSettings.json 內的 EnvironmentSample profile 建立自訂環境變數 SecretKey,Staging profile 內故意沒有設定,當 profile 內有設定,會以 profile 為主,沒有才會使用 Windows 環境變數
namespace EnvironmentsSample
{
    public class Program
    {
        public static void Main(string[] args)
        {
            Console.WriteLine("----- 自訂環境變數:SecretKey -----");
            string environment = Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT");
            string secretKey = Environment.GetEnvironmentVariable("SecretKey");
            Console.WriteLine($"Environment.GetEnvironmentVariable(\"DOTNET_ENVIRONMENT\"):{environment}");
            Console.WriteLine($"SecretKey:{secretKey}");
        }
    }
}
下圖是使用 EnvironmentSample profile 和 Staging profile 的疊圖
  • EnvironmentSample profile:讀取 launchSettings 內的 SecretKey
  • Staging profile:讀取 Windows 環境變數

部屬為 Windows Service

參考 [.NET] 使用 BackgroundService 建立 Windows Service 把 Worker Service 註冊為 Windows Service,觀察部屬後抓到的 appsettings.json

Program 內設定為 Windows Service
namespace EnvironmentsSample
{
    public class Program
    {
        public static void Main(string[] args)
        {
            HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);

            builder.Services.AddWindowsService(options =>
            {
                options.ServiceName = "EnvironmentsService";
            });

            builder.Services.AddHostedService<Worker>();

            var host = builder.Build();
            host.Run();
        }
    }
}
Worker 內注入 configuration,並輸出相關資訊
using System.Text;

namespace EnvironmentsSample
{
    public class Worker : BackgroundService
    {
        private readonly ILogger<Worker> _logger;
        private readonly IConfiguration _configuration;

        public Worker(ILogger<Worker> logger, IConfiguration configuration)
        {
            _logger = logger;
            _configuration = configuration;
        }

        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            string environment = _configuration["DOTNET_ENVIRONMENT"];
            string secretKey = _configuration["SecretKey"];
            string connectionString = _configuration.GetConnectionString("SqlServer");

            StringBuilder sb = new StringBuilder();
            sb.AppendLine($"Worker running at: {DateTimeOffset.Now}");
            sb.AppendLine($"DOTNET_ENVIRONMENT:{environment}");
            sb.AppendLine($"SecretKey:{secretKey}");
            sb.AppendLine($"ConnectionString:{connectionString}");

            _logger.LogError(sb.ToString());
        }
    }
}

沒有留言:

張貼留言