![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiIx0DciV2dmADM30zd-cmbw5ib1c0Y1VVbjZnTXxEMW1mY0F0MjhWMTp1asJzY1x2RPp3aU1keBR0T31ERNdXS6hld5M0T1UERPFTR6x0bOdlWwoEWZZHMyIma5EzYulzRilWNykldwIjYqVTej5WOHJWa1ITW11EWa5mRXJGc5kHT20ESjBjUIF2Lc12bj5SYphXa5VWen5WY35iclN3Ztl2Lc9CX6MHc0RHaiojIsJye.png)
IHostBuilder接口中定義了ConfigureHostConfiguration方法和ConfigureAppConfiguration方法,它們可以幫助我們設定面向宿主(IHost對象)和應用(承載服務)的配置。針對配置的初始化也可以借助IWebHostBuilder接口來完成。
通過《服務承載系統[2]: 承載長時間運作的服務[下篇]》的介紹可知,IHostBuilder接口中定義了ConfigureHostConfiguration方法和ConfigureAppConfiguration方法,它們可以幫助我們設定面向宿主(IHost對象)和應用(承載服務)的配置。針對配置的初始化也可以借助IWebHostBuilder接口來完成。
目錄
一、初始化配置
二、以鍵值對形式讀取和修改配置
三、合并配置
四、注冊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]:如何放置你的初始化代碼