Skip to main content

配置

分类:  Asp.net Core入门 标签:  #Asp.Net core基础 #基础 #Web 发布于: 2023-06-04 20:24:03

ASP.net Core基于Configuration Provider提供配置服务,Configuration Provider从配置源(Configuration source)读取键值对的配置,并提供给Asp.net Core。 默认支持如下的配置源:

  • 配置文件: appsettings.json
  • 环境变量
  • Azure Key Vault
  • Azure App Configuration服务
  • 命令行参数
  • 自定义的配置提供者
  • 目录文件
  • 内存对象

缺省配置

关于缺省的配置,我们从dotnet new或者Visual Studio创建的模板:

public class Program
{
    public static void Main(string[] args)
    {
        CreateHostBuilder(args).Build().Run();
    }

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseStartup<Startup>();
            });
}

CreateDefaultBuilder按照如下的顺序向应用提供配置:

  • 向应用中添加ChainedConfigurationProvider, 添加一个IConfiguration的实例,并且向其中添加Host的配置,并且将它设置为第一个配置源(需要注意的是Host的配置是从环境变量和命令行参数中读入的)。
  • appsettings.json: 使用JSON Configuration Provider
  • appsetttings.{Environment}.json。
  • App secrets (仅仅在开发环境下)
  • 环境变量读入
  • 命令行参数读入

Tips
需要注意添加的顺序,后面添加的如果有同样名称的配置,后面的覆盖前面的。

分层的配置

需要了解的是配置的键值形式:

  • 键是分层的,在ASP.net Core中分层以冒号分隔,另外需要注意的环境变量中有些不一样的地方。
  • 所有的配置值都是默认字符串类型。

关于键的分层,我们先看一个配置文件的例子:

  "Position": {
    "Title": "Editor",
    "Name": "Joe Smith"
  }

这个配置里有两对配置,分别键值对应为:

key = "Position:Title", Value="Editor"
key = "Position:Name",  Value="Joe Smith"

另外需要注意的是目前在大多数的配置提供者中都是以冒号":"来分隔,但是在环境变量里,冒号不是所有的平台都支持,但是双下划线所有平台都支持,并且环境变量里的双下划线"__" 会在ASP.net Core里自动转为":"

另外需要注意的是,在.Net中默认定义环境变量前缀为DOTNET__, 在Asp.net Core中环境变量前缀定义为`ASPNETCORE__'

所以上述的环境变量中可以这样定义:

export DOTNET_Position__Title="Editor" 
export DONTNET_Position__Name="Joe Smith"

或者在Asp.net Core中:

export ASPNETCORE_Position__Title="Editor" 
export ASPNETCORE_Position__Name="Joe Smith"

需要注意的是这个默认的前缀是可以更改的,可以通过如下的形式进行更改:

public class Program
{
    public static void Main(string[] args)
    {
        CreateHostBuilder(args).Build().Run();
    }

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureAppConfiguration((hostingContext, config) =>
            {
                config.AddEnvironmentVariables(prefix: "MyCustomPrefix_");
            })
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseStartup<Startup>();
            });
}

使用环境变量标识一个数组,例如:

{
    "SmtpServer": "smtp.example.com",
    "Logging": [
        {
            "Name": "ToEmail",
            "Level": "Critical",
            "Args": {
                "FromAddress": "MySystem@example.com",
                "ToAddress": "SRE@example.com"
            }
        },
        {
            "Name": "ToConsole",
            "Level": "Information"
        }
    ]
}

环境变量表示为:

setx SmtpServer=smtp.example.com
setx Logging__0__Name=ToEmail
setx Logging__0__Level=Critical
setx Logging__0__Args__FromAddress=MySystem@example.com
setx Logging__0__Args__ToAddress=SRE@example.com
setx Logging__1__Name=ToConsole
setx Logging__1__Level=Information

使用命令行参数

很简单,看两个例子:

dotnet run MyKey="My key from command line" Position:Title=Cmd Position:Name=Cmd_Rick
dotnet run /MyKey "Using /" /Position:Title=Cmd_ /Position:Name=Cmd_Rick

或者:

dotnet run --MyKey "Using --" --Position:Title=Cmd-- --Position:Name=Cmd--Rick

使用DI来使用配置服务

我们在Host那一节里已经学习过了,配置服务是几个在Host初始化的时候已经初始化的服务之一,因此它可以很容易的在Startup所有方法中使用,我们来看一个简单的例子:

{
  "Position": {
    "Title": "Editor",
    "Name": "Joe Smith"
  },
  "MyKey":  "My appsettings.json Value",
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*"
}

我们看一下如何从DI中使用这个配置:

public class TestModel : PageModel
{
    // requires using Microsoft.Extensions.Configuration;
    private readonly IConfiguration Configuration;

    public TestModel(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public ContentResult OnGet()
    {
        var myKeyValue = Configuration["MyKey"];
        var title = Configuration["Position:Title"];
        var name = Configuration["Position:Name"];
        var defaultLogLevel = Configuration["Logging:LogLevel:Default"];


        return Content($"MyKey value: {myKeyValue} \n" +
                       $"Title: {title} \n" +
                       $"Name: {name} \n" +
                       $"Default Log Level: {defaultLogLevel}");
    }
}

键和值的说明

  • 键和值都不区分大小写。
  • 如果在多个配置提供者中提供了同样的键,那么按照之间的顺序,后面提供的值覆盖之前的配置提供者。
  • 键的分级(分层)
    • 在配置API中以冒号":"作为分层分隔符
    • 在环境变量里使用双下划线作为分隔符"__"
    • Azure Key Vault中,分层分隔符使用"--"
  • 配置绑定支持数组绑定,请参考之前的数组绑定的方法。

链接字符串 (Connect String)

在环境变量里有四类前缀的关于链接字符串的特殊变量,在环境变量里以:

  • CUSTOMCONNSTR_{KEY}
  • MYSQLCONNSTR_{KEY}
  • SQLAZURECONNSTR_{KEY}
  • SQLCONNSTR_{KEY}

这四中读入之后,移除前缀,后面的key,放入到配置中的ConnectionStrings 节中。

文件配置源

文件配置源包括如下几种:

  • INI文件配置源
  • JSON文件配置源
  • XML文件配置源

给出一个实例,如何添加新的配置提供者:

public class Program
{
    public static void Main(string[] args)
    {
        CreateHostBuilder(args).Build().Run();
    }

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureAppConfiguration((hostingContext, config) =>
            {
                config.Sources.Clear();

                var env = hostingContext.HostingEnvironment;

                config.AddIniFile("MyIniConfig.ini", optional: true, reloadOnChange: true)
                      .AddIniFile($"MyIniConfig.{env.EnvironmentName}.ini",
                                     optional: true, reloadOnChange: true);

                config.AddEnvironmentVariables();

                if (args != null)
                {
                    config.AddCommandLine(args);
                }
            })
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseStartup<Startup>();
            });
}

需要注意的是HostBuilder的方法`ConfigureAppConfiguration(hostingcontext, config)

如下是INI文件的格式:

MyKey="MyIniConfig.ini Value"

[Position]
Title="My INI Config title"
Name="My INI Config name"

[Logging:LogLevel]
Default=Information
Microsoft=Warning

如下是XML的配置:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
  <section name="section0">
    <key name="key0">value 00</key>
    <key name="key1">value 01</key>
  </section>
  <section name="section1">
    <key name="key0">value 10</key>
    <key name="key1">value 11</key>
  </section>
</configuration>

其他两种基本类似。

Kestrel Endpoint配置

有三种方式用于配置Kestrel的Endpoint:

  • 扩展方法UseUrls
  • 命令行参数---urls
  • 环境变量ASPNETCORE_URLS
  • 配置文件

Examples:

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseStartup<Startup>();
		webBuilder..UseUrls("http://*:5000;http://localhost:5001;https://hostname:5002");
            });
dotnet run --urls="https://localhost:7777"
set Kestrel__Endpoints__Https__Url=https://localhost:8888
  "Kestrel": {
    "Endpoints": {
      "Https": {
        "Url": "https://localhost:9999"
      }
    }
  }

IConfiguration的方法

Configuration提供了一些方法用户配置。

GetValue

实例:

public class TestNumModel : PageModel
{
    private readonly IConfiguration Configuration;

    public TestNumModel(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public ContentResult OnGet()
    {
        var number = Configuration.GetValue<int>("NumberKey", 99);
        return Content($"{number}");
    }
}

GetSection, GetChildren, and Exists

我们按照上述顺序一一看下demo code:

public class Program
{
    public static void Main(string[] args)
    {
        CreateHostBuilder(args).Build().Run();
    }

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureAppConfiguration((hostingContext, config) =>
            {
                config.AddJsonFile("MySubsection.json", 
                    optional: true, 
                    reloadOnChange: true);
            })
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseStartup<Startup>();
            });
}

载入配置,并且配置了属性optionalreloadOnChange

public class TestSectionModel : PageModel
{
    private readonly IConfiguration Config;

    public TestSectionModel(IConfiguration configuration)
    {
        Config = configuration.GetSection("section1");
    }

    public ContentResult OnGet()
    {
        return Content(
                $"section1:key0: '{Config["key0"]}'\n" +
                $"section1:key1: '{Config["key1"]}'");
    }
}

GetChildren

public class TestSection4Model : PageModel
{
    private readonly IConfiguration Config;

    public TestSection4Model(IConfiguration configuration)
    {
        Config = configuration;
    }

    public ContentResult OnGet()
    {
        string s = null;
        var selection = Config.GetSection("section2");
        if (!selection.Exists())
        {
            throw new System.Exception("section2 does not exist.");
        }
        var children = selection.GetChildren();

        foreach (var subSection in children)
        {
            int i = 0;
            var key1 = subSection.Key + ":key" + i++.ToString();
            var key2 = subSection.Key + ":key" + i.ToString();
            s += key1 + " value: " + selection[key1] + "\n";
            s += key2 + " value: " + selection[key2] + "\n";
        }
        return Content(s);
    }
}

存取配置值

Startup类中

Startup类中存取配置。由于配置在host配置里就已经初始化了,因此在startup类中可以通过DI存取:

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddRazorPages();
        Console.WriteLine($"MyKey : {Configuration["MyKey"]}");
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        Console.WriteLine($"Position:Title : {Configuration["Position:Title"]}");

        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseExceptionHandler("/Error");
            app.UseHsts();
        }

        app.UseHttpsRedirection();
        app.UseStaticFiles();

        app.UseRouting();

        app.UseAuthorization();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapRazorPages();
        });
    }
}

Razor Page

先引用类:

@page
@model Test5Model
@using Microsoft.Extensions.Configuration
@inject IConfiguration Configuration

Configuration value for 'MyKey': @Configuration["MyKey"]

MVC View file

@using Microsoft.Extensions.Configuration
@inject IConfiguration Configuration

Configuration value for 'MyKey': @Configuration["MyKey"]

在控制器中添加配置值

关于这个部分可以参考基于Ihosting Startup 程序集

我们除了直接引用Iconfiguration的程序实例之外,还可以使用Option模式来存取配置,我们下一节来学习Options模式