在上一篇 [.NETCore] ASP.NET Core - ENVIRONMENT 提到了如何設定環境變數值,今天要提的也是與 Configuration 相關的,在開發時往往會因為不同環境讀取不同 Config 的需求,舉例來說如果 Development 與 Staging 環境的資料庫不同時,就會有在不同環境讀取各自的 DB 連線字串設定需求,這篇就介紹如何透過 .NET Core 專案預設的 appsettings.json 在不同環境設定及讀取相對應的組態設定檔資訊,若有問題或是錯誤的地方歡迎各位高手給予指導。
appsettings.json
首先,建立一個 .NET Core Application 應用程式 ( 測試的版本為 2.2 ),在建立完畢之後可以看到專案的 program.cs 預設內容如下
using Microsoft.AspNetCore; using Microsoft.AspNetCore.Hosting; namespace CoreLabApplication { public class Program { public static void Main(string[] args) { CreateWebHostBuilder(args).Build().Run(); } public static IWebHostBuilder CreateWebHostBuilder(string[] args) => WebHost.CreateDefaultBuilder(args) .UseStartup<Startup>(); } }
在程式主要進入點一開始先呼叫靜態方法 CreateWebHostBuilder ,其內容是新增一個 default 的 Web Host Builder,並建立預設的 Configuration 與 Settings 資訊,在透過 IWebHostBuilder 中的 UseStartup 擴充方法指定 TStartUp 為同樣是內建的 StartUp 類別,接著使用 build 方法建立 WebHost。剛剛提到 CreateWebHostBuilder 會建立預設的 Configuration 資訊,接著我們為了瞭解實作細節透過反射查看此方法骨子裡面做了麼事
appsettings.json 預設配置檔案
- config.AddJsonFile("appsettings.json", true, true)
首先 config Provider 來源設定為 Json 格式,並使用內建的 appsettings.json 作為 config 來源,並定義 reloadOnChange 為 true,也就是當檔案內容更新時後會重新載入。
- AddJsonFile("appsettings." + hostingEnvironment.EnvironmentName + ".json", true, true);
接著會在使用環境變數尋找相關環境設定檔,如果存在就覆蓋原本的預設值,舉例來說,在 Development 環境底下,此代碼就會尋找根目錄中是否存在 appsettings.Development.json 檔案,如果 appsettings.json 與 appsettings.Development.json 有設定值相同時,會以後面的值為主 (覆蓋前者)。
備註 : 在 .NET Core 2.1 後的版本使用 IWebHostBuilder 取代既有的 IWebHost 方式,在過去如果要根據環境讀取相關 appsettings.json 資訊 ( 或是 Console )大多都要自行撰寫下列代碼
Configuration 載入順序
在 .NET Core 中提供不同 Provider 方式讀取/載入相關設定資訊,舉例來說在上述代碼中就可以看到有用到 CommandLine、Environment Variable、File (.Json)、UserSecrets 等多種方式,如果在自訂時也可以參考此種設定,詳細可以參考 MSDN 說明 : 傳送門,雖然有多種載入 Configuration 的方式,但讀取的優先順序也是很重要的,在啟動時後面載入的會將前者重複定義的覆蓋掉,因此在 Configuration 載入時載入順序如下
新增設定 /// <summary> /// Initializes a new instance of the <see cref="T:Microsoft.AspNetCore.Hosting.WebHostBuilder" /> class with pre-configured defaults. /// </summary> /// <param name="args">The command line args.</param> /// <returns>The initialized <see cref="T:Microsoft.AspNetCore.Hosting.IWebHostBuilder" />.</returns> public static IWebHostBuilder CreateDefaultBuilder(string[] args) { WebHostBuilder hostBuilder = new WebHostBuilder(); if (string.IsNullOrEmpty(hostBuilder.GetSetting(WebHostDefaults.ContentRootKey))) hostBuilder.UseContentRoot(Directory.GetCurrentDirectory()); if (args != null) hostBuilder.UseConfiguration((IConfiguration) new ConfigurationBuilder().AddCommandLine(args).Build()); hostBuilder.UseKestrel((Action<WebHostBuilderContext, KestrelServerOptions>) ((builderContext, options) => options.Configure((IConfiguration) builderContext.Configuration.GetSection("Kestrel")))).ConfigureAppConfiguration((Action<WebHostBuilderContext, IConfigurationBuilder>) ((hostingContext, config) => { IHostingEnvironment hostingEnvironment = hostingContext.HostingEnvironment; config.AddJsonFile("appsettings.json", true, true) .AddJsonFile("appsettings." + hostingEnvironment.EnvironmentName + ".json", true, true); if (hostingEnvironment.IsDevelopment()) { Assembly assembly = Assembly.Load(new AssemblyName(hostingEnvironment.ApplicationName)); if (assembly != (Assembly) null) config.AddUserSecrets(assembly, true); } config.AddEnvironmentVariables(); if (args == null) return; config.AddCommandLine(args); })).ConfigureLogging((Action<WebHostBuilderContext, ILoggingBuilder>) ((hostingContext, logging) => { logging.AddConfiguration((IConfiguration) hostingContext.Configuration.GetSection("Logging")); logging.AddConsole(); logging.AddDebug(); logging.AddEventSourceLogger(); })).ConfigureServices((Action<WebHostBuilderContext, IServiceCollection>) ((hostingContext, services) => { services.PostConfigure<HostFilteringOptions>((Action<HostFilteringOptions>) (options => { if (options.AllowedHosts != null && options.AllowedHosts.Count != 0) return; string str = hostingContext.Configuration["AllowedHosts"]; string[] strArray1; if (str == null) strArray1 = (string[]) null; else strArray1 = str.Split(new char[1]{ ';' }, StringSplitOptions.RemoveEmptyEntries); string[] strArray2 = strArray1; HostFilteringOptions filteringOptions = options; string[] strArray3; if (strArray2 == null || strArray2.Length == 0) strArray3 = new string[1]{ "*" }; else strArray3 = strArray2; filteringOptions.AllowedHosts = (IList<string>) strArray3; })); services.AddSingleton<IOptionsChangeTokenSource<HostFilteringOptions>>((IOptionsChangeTokenSource<HostFilteringOptions>) new ConfigurationChangeTokenSource<HostFilteringOptions>(hostingContext.Configuration)); services.AddTransient<IStartupFilter, HostFilteringStartupFilter>(); })).UseIIS().UseIISIntegration().UseDefaultServiceProvider((Action<WebHostBuilderContext, ServiceProviderOptions>) ((context, options) => options.ValidateScopes = context.HostingEnvironment.IsDevelopment())); return (IWebHostBuilder) hostBuilder; }透過反射可以得知主要是建立一個 WebHostBuilder 並定義相關設定初始值,由於代碼細節過多因此整理 CreateWebHostBuilder 方法重點如下
appsettings.json 預設配置檔案
- config.AddJsonFile("appsettings.json", true, true)
首先 config Provider 來源設定為 Json 格式,並使用內建的 appsettings.json 作為 config 來源,並定義 reloadOnChange 為 true,也就是當檔案內容更新時後會重新載入。
- AddJsonFile("appsettings." + hostingEnvironment.EnvironmentName + ".json", true, true);
接著會在使用環境變數尋找相關環境設定檔,如果存在就覆蓋原本的預設值,舉例來說,在 Development 環境底下,此代碼就會尋找根目錄中是否存在 appsettings.Development.json 檔案,如果 appsettings.json 與 appsettings.Development.json 有設定值相同時,會以後面的值為主 (覆蓋前者)。
備註 : 在 .NET Core 2.1 後的版本使用 IWebHostBuilder 取代既有的 IWebHost 方式,在過去如果要根據環境讀取相關 appsettings.json 資訊 ( 或是 Console )大多都要自行撰寫下列代碼
var builder = new ConfigurationBuilder() .SetBasePath(env.ContentRootPath) .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true) .Build();在.NET Core 2.1 之後則提供靜態方法 CreateWebHostBuilder 將常用 Configuration 做設定,詳細可以參考 Migrate from ASP.NET Core 2.0 to 2.1
Configuration 載入順序
在 .NET Core 中提供不同 Provider 方式讀取/載入相關設定資訊,舉例來說在上述代碼中就可以看到有用到 CommandLine、Environment Variable、File (.Json)、UserSecrets 等多種方式,如果在自訂時也可以參考此種設定,詳細可以參考 MSDN 說明 : 傳送門,雖然有多種載入 Configuration 的方式,但讀取的優先順序也是很重要的,在啟動時後面載入的會將前者重複定義的覆蓋掉,因此在 Configuration 載入時載入順序如下
- File 檔案
- Azure Key Vault
- User Secrets ( 僅 Development 環境 )
- 環境變數
- 命令列
透過以上詳細說明,可以得知如果要設定不同環境的設定檔時,僅需要加上 appsettings.{環境變數}.json 即可,就可以透過內建的組態設定機制來區分環境,取得 appsettings.json 方式之前有介紹過,這裡就不在多加說明,詳細可以參考 傳送門,下列為新增 Development 與 Production 範例
縮排設定
如果想要調整 appsettings.json 縮排將前墜詞相同的放一起,可以透過以下步驟調整
Step 1 : 開啟 Visual Studio 2019,在專案上滑鼠 double click 開啟專案 csproj
Step 2 : 加上以下代碼
<ItemGroup> <Content Include="appsettings.json"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </Content> <Content Include="appsettings.Development.json;appsettings.Production.json"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <DependentUpon>appsettings.json</DependentUpon> </Content> </ItemGroup>設定完成 !!! 設定成功畫面如下
心得
在查詢 appsettings 相關文章時大多都還是舊語法相關的資料,且日期多為 2018 年中左右,沒一年的時間就已經出新的語法版本從 2.1 到目前的最新的 3.0 preview,目前在查詢相關文件時還是以 MSDN 為主,避免得到錯誤的資訊造成誤判,希望自己可以盡快補齊 .NET Core 相關資訊,繼續努力往自己的目標邁進 :)
參考
ASP.NET Core 的設定
What is the difference between IWebHost WebHostBuilder BuildWebHost
0 意見:
張貼留言