通过《服务承载系统[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字符串直接作为请求的响应内容。
代码语言:javascript复制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方法达到相同的目的。
代码语言:javascript复制public interface IWebHostBuilder
{
string GetSetting(string key);
IWebHostBuilder UseSetting(string key, string value);
...
}
上面演示的实例采用环境变量来提供最终绑定为FoobarOptions对象的原始配置,这样的配置数据也可以通过如下所示的方式调用IWebHostBuilder接口的UseSetting方法来提供。修改后的应用程序启动之后,如果利用浏览器访问该应用,同样可以得到上图所示的输出结果。
代码语言:javascript复制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,该静态类型中还提供了其他一些预定义的配置项名称,所以这也是一个比较重要的类型。
代码语言:javascript复制public static class WebHostDefaults
{
public static readonly string ServerUrlsKey = "urls";
...
}
针对上面演示的这个实例,如果希望为服务器设置不同的监听地址,直接调用IWebHostBuilder接口的UseSetting方法将新的地址作为urls配置项的内容即可。既然配置项被命名为urls,就意味着服务器的监听地址不仅限于一个,如果希望设置多个监听地址,我们可以采用分号作为分隔符。
代码语言:javascript复制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类型的异常。基础路径可以通过注册中间件的方式进行设置。
代码语言:javascript复制public static class HostingAbstractionsWebHostBuilderExtensions
{
public static IWebHostBuilder UseUrls(this IWebHostBuilder hostBuilder, params string[] urls);
}
三、合并配置
在启动一个ASP.NET Core应用时,我们可以自行创建一个承载配置的IConfiguration对象,并通过调用IWebHostBuilder接口的UseConfiguration扩展方法将它与应用自身的配置进行合并。如果应用自身存在重复的配置项,那么该配置项的值会被指定的IConfiguration对象覆盖。
代码语言:javascript复制public static class HostingAbstractionsWebHostBuilderExtensions
{
public static IWebHostBuilder UseConfiguration(this IWebHostBuilder hostBuilder, IConfiguration configuration);
}
如果前面演示的实例需要采用这种方式来提供配置,我们可以对程序代码做如下修改。如下面的代码片段所示,我们创建了一个ConfigurationBuilder对象,并通过调用AddInMemory
Collection扩展方法注册了一个MemoryConfigurationSource对象,它提供了绑定FoobarOptions对象所需的所有配置数据。我们最终利用ConfigurationBuilder创建出一个IConfiguration对象,并通过调用上述UseConfiguration方法将提供的配置数据合并到当前应用中。修改后的应用程序启动之后,如果利用浏览器访问该应用,同样会得到图11-12所示的输出结果。(S1115)
代码语言:javascript复制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>。
代码语言:javascript复制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方法的调用,依然可以达到相同的目的。修改后的应用程序启动之后,如果利用浏览器访问该应用,同样会得到上图所示的输出结果。
代码语言:javascript复制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]:如何放置你的初始化代码