ASP.NET Core應用基本程式設計模式[3]:配置多種使用形式
通過《服務承載系統[2]: 承載長時間執行的服務[下篇]》的介紹可知,IHostBuilder介面中定義了ConfigureHostConfiguration方法和ConfigureAppConfiguration方法,它們可以幫助我們設定面向宿主(IHost物件)和應用(承載服務)的配置。針對配置的初始化也可以藉助IWebHostBuilder介面來完成。[本文節選自《ASP.NET Core 3框架揭祕》第11章, 更多關於ASP.NET Core的文章請點這裡]
目錄
一、初始化配置
二、以鍵值對形式讀取和修改配置
三、合併配置
四、註冊IConfigurationSource
一、初始化配置
當IWebHostBuilder物件被建立的時候,它會將當前的環境變數作為配置源來建立承載最初配置資料的IConfiguration物件,但它只會選擇名稱以“ASPNETCORE_”為字首的環境變數(通過靜態型別Host的CreateDefaultBuilder方法建立的HostBuilder預設選擇的是字首為“DOTNET_”的環境變數)。在演示針對環境變數的初始化配置之前,需要先解決配置的消費問題,即如何獲取配置資料。
前面演示了針對Startup型別的建構函式注入,表示配置的IConfiguration物件是能夠注入Startup型別建構函式中的兩個服務物件之一。接下來我們採用Options模式來消費以環境變數形式提供的配置,如下所示的FoobarOptions是我們定義的Options型別。在註冊的Startup型別中,可以直接在建構函式中注入IConfiguration服務,並在ConfigureServices方法中將其對映為FoobarOptions型別。在Configure方法中,可以通過注入的IOptions<FoobarOptions>服務得到通過配置繫結的FoobarOptions物件,並將其序列化成JSON字串。在通過呼叫IApplicationBuilder的Run方法註冊的中介軟體中,這個JSON字串直接作為請求的響應內容。
class Program { static void Main() { Environment.SetEnvironmentVariable("ASPNETCORE_FOOBAR:FOO", "Foo"); Environment.SetEnvironmentVariable("ASPNETCORE_FOOBAR:BAR", "Bar"); Environment.SetEnvironmentVariable("ASPNETCORE_Baz", "Baz"); Host.CreateDefaultBuilder().ConfigureWebHostDefaults(builder => builder.UseStartup<Startup>()) .Build() .Run(); } public class Startup { private readonly IConfiguration _configuration; public Startup(IConfiguration configuration) => _configuration = configuration; public void ConfigureServices(IServiceCollection services) => services.Configure<FoobarOptions>(_configuration); public void Configure(IApplicationBuilder app, IOptions<FoobarOptions> optionsAccessor) { var options = optionsAccessor.Value; var json = JsonConvert.SerializeObject(options, Formatting.Indented); app.Run(async context => { context.Response.ContentType = "text/html"; await context.Response.WriteAsync($"<pre>{json}</pre>"); }); } } public class FoobarOptions { public Foobar Foobar { get; set; } public string Baz { get; set; } } public class Foobar { public string Foo { get; set; } public string Bar { get; set; } } }
為了能夠提供繫結為FoobarOptions物件的原始配置,我們在Main方法中設定了3個對應的環境變數,這些環境變數具有相同的字首“ASPNETCORE_”。應用程式啟動之後,如果利用瀏覽器訪問該應用,得到的輸出結果如下圖所示。
二、以鍵值對形式讀取和修改配置
《配置[3]:配置模型總體設計》對配置模型進行了深入分析,由此可知,IConfiguration物件是以字典的結構來儲存配置資料的,該介面定義的索引可供我們以鍵值對的形式來讀取和修改配置資料。在ASP.NET Core應用中,我們可以通過呼叫定義在IWebHostBuilder介面的GetSetting方法和UseSetting方法達到相同的目的。
public interface IWebHostBuilder { string GetSetting(string key); IWebHostBuilder UseSetting(string key, string value); ... }
上面演示的例項採用環境變數來提供最終繫結為FoobarOptions物件的原始配置,這樣的配置資料也可以通過如下所示的方式呼叫IWebHostBuilder介面的UseSetting方法來提供。修改後的應用程式啟動之後,如果利用瀏覽器訪問該應用,同樣可以得到上圖所示的輸出結果。
class Program { static void Main() { Host.CreateDefaultBuilder().ConfigureWebHostDefaults(builder => builder .UseSetting("Foobar:Foo", "Foo") .UseSetting("Foobar:Bar", "Bar") .UseSetting("Baz", "Baz") .UseStartup<Startup>()) .Build() .Run(); } }
配置不僅僅供應用程式來使用,ASP.NET Core框架自身的很多特性也都可以通過配置進行定製。如果希望通過修改配置來控制ASP.NET Core框架的某些行為,就需要先知道對應的配置項的名稱是什麼。例如,ASP.NET Core應用的伺服器預設使用launchSettings.json檔案定義的監聽地址,但是我們可以通過修改配置採用其他的監聽地址。包括埠在內的監聽地址是通過名稱為urls的配置項來控制的,如果記不住這個配置項的名稱,也可以直接使用定義在WebHostDefaults中對應的只讀屬性ServerUrlsKey,該靜態型別中還提供了其他一些預定義的配置項名稱,所以這也是一個比較重要的型別。
public static class WebHostDefaults { public static readonly string ServerUrlsKey = "urls"; ... }
針對上面演示的這個例項,如果希望為伺服器設定不同的監聽地址,直接呼叫IWebHostBuilder介面的UseSetting方法將新的地址作為urls配置項的內容即可。既然配置項被命名為urls,就意味著伺服器的監聽地址不僅限於一個,如果希望設定多個監聽地址,我們可以採用分號作為分隔符。
class Program { static void Main() { Host.CreateDefaultBuilder().ConfigureWebHostDefaults(builder => builder .UseSetting("Foobar:Foo", "Foo") .UseSetting("Foobar:Bar", "Bar") .UseSetting("Baz", "Baz") .UseSetting("urls", "http://0.0.0.0:8888;http://0.0.0.0:9999") .UseStartup<Startup>()) .Build() .Run(); } }
為了使例項程式採用不同的監聽地址,可以採用如上所示的方式呼叫IWebHostBuilder介面的UseSetting方法設定兩個針對8888和9999埠號的監聽地址。由圖11-13所示的程式啟動後的輸出結果可以看出,伺服器確實採用我們指定的兩個地址監聽請求,通過瀏覽器針對這兩個地址傳送的請求能夠得到相同的結果。
除了呼叫UseSetting方法設定urls配置項來修改伺服器的監聽地址,直接呼叫IWebHostBuilder介面的UseUrls擴充套件方法也可以達到相同的目的。另外,我們提供的監聽地址只能包含主機名稱/IP地址(Host/IP)和埠號,不能包含基礎路徑(PathBase)。如果我們提供“http://0.0.0.0/3721/foobar”這樣一個URL,系統會丟擲一個InvalidOperationException型別的異常。基礎路徑可以通過註冊中介軟體的方式進行設定。
public static class HostingAbstractionsWebHostBuilderExtensions { public static IWebHostBuilder UseUrls(this IWebHostBuilder hostBuilder, params string[] urls); }
三、合併配置
在啟動一個ASP.NET Core應用時,我們可以自行建立一個承載配置的IConfiguration物件,並通過呼叫IWebHostBuilder介面的UseConfiguration擴充套件方法將它與應用自身的配置進行合併。如果應用自身存在重複的配置項,那麼該配置項的值會被指定的IConfiguration物件覆蓋。
public static class HostingAbstractionsWebHostBuilderExtensions { public static IWebHostBuilder UseConfiguration(this IWebHostBuilder hostBuilder, IConfiguration configuration); }
如果前面演示的例項需要採用這種方式來提供配置,我們可以對程式程式碼做如下修改。如下面的程式碼片段所示,我們建立了一個ConfigurationBuilder物件,並通過呼叫AddInMemory
Collection擴充套件方法註冊了一個MemoryConfigurationSource物件,它提供了繫結FoobarOptions物件所需的所有配置資料。我們最終利用ConfigurationBuilder創建出一個IConfiguration物件,並通過呼叫上述UseConfiguration方法將提供的配置資料合併到當前應用中。修改後的應用程式啟動之後,如果利用瀏覽器訪問該應用,同樣會得到圖11-12所示的輸出結果。(S1115)
class Program { static void Main() { var configuration = new ConfigurationBuilder().AddInMemoryCollection(new Dictionary<string, string> { ["Foobar:Foo"] = "Foo", ["Foobar:Bar"] = "Bar", ["Baz"] = "Baz" }) .Build(); Host.CreateDefaultBuilder().ConfigureWebHostDefaults(builder => builder .UseConfiguration(configuration) .UseStartup<Startup>()) .Build() .Run(); } }
四、註冊IConfigurationSource
配置系統最大的特點是可以註冊不同的配置源。藉助IWebHostBuilder介面的UseConfiguration擴充套件方法,雖然可以將利用配置系統提供的IConfiguration物件應用到ASP.NET Core程式中,但是這樣的整合方式總顯得不夠徹底,更加理想的方式應該是可以直接在ASP.NET Core應用中註冊IConfigurationSource物件。
針對IConfigurationSource的註冊可以呼叫IWebHostBuilder介面的ConfigureAppConfiguration方法來完成,該方法與在IHostBuilder介面上定義的同名方法基本上是等效的。如下面的程式碼片段所示,這個方法的引數是一個型別為Action<WebHostBuilderContext, IConfigurationBuilder>的委託物件,這意味著我們可以就承載上下文對配置做針對性設定。如果設定與當前承載上下文無關,我們還可以呼叫ConfigureAppConfiguration方法過載,該方法的引數型別為Action<IConfigurationBuilder>。
public interface IWebHostBuilder { IWebHostBuilder ConfigureAppConfiguration(Action<WebHostBuilderContext, IConfigurationBuilder> configureDelegate); } public static class WebHostBuilderExtensions { public static IWebHostBuilder ConfigureAppConfiguration(this IWebHostBuilder hostBuilder, Action<IConfigurationBuilder> configureDelegate); }
對於上面演示的這個程式來說,如果將針對IWebHostBuilder介面的UseConfiguration方法的呼叫替換成如下所示的針對ConfigureAppConfiguration方法的呼叫,依然可以達到相同的目的。修改後的應用程式啟動之後,如果利用瀏覽器訪問該應用,同樣會得到上圖所示的輸出結果。
class Program { static void Main() { Host.CreateDefaultBuilder().ConfigureWebHostDefaults(builder => builder.ConfigureAppConfiguration(config => config .AddInMemoryCollection(new Dictionary<string, string> { ["Foobar:Foo"] = "Foo", ["Foobar:Bar"] = "Bar", ["Baz"] = "Baz" })) .UseStartup<Startup>()) .Build() .Run(); } }
ASP.NET Core程式設計模式[1]:管道式的請求處理
ASP.NET Core程式設計模式[2]:依賴注入的運用
ASP.NET Core程式設計模式[3]:配置多種使用形式
ASP.NET Core程式設計模式[4]:基於承載環境的程式設計
ASP.NET Core程式設計模式[5]:如何放置你的初始化程式碼