artech

通过《服务承载系统[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_”。应用程序启动之后,如果利用浏览器访问该应用,得到的输出结果如下图所示。

12

二、以键值对形式读取和修改配置

配置[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所示的程序启动后的输出结果可以看出,服务器确实采用我们指定的两个地址监听请求,通过浏览器针对这两个地址发送的请求能够得到相同的结果。

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]:如何放置你的初始化代码

相关文章: