只有累積,沒有奇蹟

2019年4月16日 星期二

[.NETCore] ASP.NET Core 環境佈署設定 appsettings.json

前言
在上一篇 [.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 資訊,接著我們為了瞭解實作細節透過反射查看此方法骨子裡面做了麼事
/// <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 意見:

張貼留言

Copyright © m@rcus 學習筆記 | Powered by Blogger

Design by Anders Noren | Blogger Theme by NewBloggerThemes.com