9

ASP.NET Core应用基本编程模式[3]:配置多种使用形式

 3 years ago
source link: https://www.cnblogs.com/artech/p/asp-net-core-program-model-3.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

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

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


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK