配置
默认设置
- 将内容根目录设置为由 GetCurrentDirectory 返回的路径。
- 通过以下项加载主机配置:
- 前缀为
DOTNET_
的环境变量。 - 命令行参数。
- 前缀为
- 通过以下对象加载应用配置:
- appsettings.json。
- appsettings.{Environment}.json。
- 密钥管理器 当应用在
Development
环境中运行时。 - 环境变量。
- 命令行参数。
- 添加以下日志记录提供程序:
- 控制台
- 调试
- EventSource
- EventLog(仅当在 Windows 上运行时)
- 当环境为“开发”时,启用范围验证和依赖关系验证。
ConfigureWebHostDefaults
方法:
- 从前缀为
ASPNETCORE_
的环境变量加载主机配置。 - 使用应用的托管配置提供程序将 Kestrel 服务器设置为 web 服务器并对其进行配置。
- 添加主机筛选中间件。
- 如果
ASPNETCORE_FORWARDEDHEADERS_ENABLED
等于true
,则添加转接头中间件。 - 支持 IIS 集成。
若要添加主机配置,请对 IHostBuilder
调用 ConfigureHostConfiguration。
通过对 IHostBuilder
调用 ConfigureAppConfiguration 创建应用配置。
appsettings.Environment
.json 值将替代 appsettings.json 中的设置
使用选项模式绑定分层配置数据
读取相关配置值的首选方法是使用选项模式。
"Position": { "Title": "Editor", "Name": "Joe Smith" } public class PositionOptions { public const string Position = "Position"; public string Title { get; set; } public string Name { get; set; } } public class Test21Model : PageModel { private readonly IConfiguration Configuration; public PositionOptions positionOptions { get; private set; } public Test21Model(IConfiguration configuration) { Configuration = configuration; } public ContentResult OnGet() { positionOptions = Configuration.GetSection(PositionOptions.Position) .Get<PositionOptions>(); return Content($"Title: {positionOptions.Title} \n" + $"Name: {positionOptions.Name}"); } }
ConfigurationBinder.Get<T>
绑定并返回指定的类型。 使用 ConfigurationBinder.Get<T>
可能比使用 ConfigurationBinder.Bind
更方便。
在上面的代码中,默认读取在应用启动后对 JSON 配置文件所做的更改。
使用选项模式时的替代方法是绑定
Position
部分并将它添加到依赖项注入服务容器。
public void ConfigureServices(IServiceCollection services) { services.Configure<PositionOptions>(Configuration.GetSection( PositionOptions.Position)); services.AddRazorPages(); } public class Test2Model : PageModel { private readonly PositionOptions _options; public Test2Model(IOptions<PositionOptions> options) { _options = options.Value; } public ContentResult OnGet() { return Content($"Title: {_options.Title} \n" + $"Name: {_options.Name}"); } }
在上面的代码中,不会读取在应用启动后对 JSON 配置文件所做的更改。 若要读取在应用启动后的更改,请使用 IOptionsSnapshot。
- 用于检索选项并管理
TOptions
实例的选项通知。 - 注册为单一实例且可以注入到任何服务生存期。
- 支持:
- 更改通知
- 命名选项
- 可重载配置
- 选择性选项失效 (IOptionsMonitorCache<TOptions>)
public class TestSnapModel : PageModel { private readonly MyOptions _snapshotOptions; public TestSnapModel(IOptionsSnapshot<MyOptions> snapshotOptionsAccessor) { _snapshotOptions = snapshotOptionsAccessor.Value; } public ContentResult OnGet() { return Content($"Option1: {_snapshotOptions.Option1} \n" + $"Option2: {_snapshotOptions.Option2}"); } }
IOptionsMonitor
和 IOptionsSnapshot
之间的区别在于:
IOptionsMonitor
是一种单一示例服务,可随时检索当前选项值,这在单一实例依赖项中尤其有用。IOptionsSnapshot
是一种作用域服务,并在构造IOptionsSnapshot<T>
对象时提供选项的快照。 选项快照旨在用于暂时性和有作用域的依赖项。
public class TestMonitorModel : PageModel { private readonly IOptionsMonitor<MyOptions> _optionsDelegate; public TestMonitorModel(IOptionsMonitor<MyOptions> optionsDelegate ) { _optionsDelegate = optionsDelegate; } public ContentResult OnGet() { return Content($"Option1: {_optionsDelegate.CurrentValue.Option1} \n" + $"Option2: {_optionsDelegate.CurrentValue.Option2}"); } }
IOptionsMonitorCache<TOptions> 由 IOptionsMonitor<TOptions> 用于缓存 TOptions
实例。 IOptionsMonitorCache<TOptions> 可使监视器中的选项实例无效,以便重新计算值 (TryRemove)。 可以通过 TryAdd 手动引入值。 在应按需重新创建所有命名实例时使用 Clear 方法。
命名选项:
- 当多个配置节绑定到同一属性时有用。
- 区分大小写。
请考虑使用以下 appsettings.json 文件:
{ "TopItem": { "Month": { "Name": "Green Widget", "Model": "GW46" }, "Year": { "Name": "Orange Gadget", "Model": "OG35" } } } public class TopItemSettings { public const string Month = "Month"; public const string Year = "Year"; public string Name { get; set; } public string Model { get; set; } } public void ConfigureServices(IServiceCollection services) { services.Configure<TopItemSettings>(TopItemSettings.Month, Configuration.GetSection("TopItem:Month")); services.Configure<TopItemSettings>(TopItemSettings.Year, Configuration.GetSection("TopItem:Year")); services.AddRazorPages(); }
通过选项验证,可以验证选项值。
public class MyConfigOptions { public const string MyConfig = "MyConfig"; [RegularExpression(@"^[a-zA-Z''-'\s]{1,40}$")] public string Key1 { get; set; } [Range(0, 1000, ErrorMessage = "Value for {0} must be between {1} and {2}.")] public int Key2 { get; set; } public int Key3 { get; set; } } public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; } public IConfiguration Configuration { get; } public void ConfigureServices(IServiceCollection services) { services.AddOptions<MyConfigOptions>() .Bind(Configuration.GetSection(MyConfigOptions.MyConfig)) .ValidateDataAnnotations(); services.AddControllersWithViews(); } public class HomeController : Controller { private readonly ILogger<HomeController> _logger; private readonly IOptions<MyConfigOptions> _config; public HomeController(IOptions<MyConfigOptions> config, ILogger<HomeController> logger) { _config = config; _logger = logger; try { var configValue = _config.Value; } catch (OptionsValidationException ex) { foreach (var failure in ex.Failures) { _logger.LogError(failure); } } }
使用委托应用更复杂的验证规则:
public void ConfigureServices(IServiceCollection services) { services.AddOptions<MyConfigOptions>() .Bind(Configuration.GetSection(MyConfigOptions.MyConfig)) .ValidateDataAnnotations() .Validate(config => { if (config.Key2 != 0) { return config.Key3 > config.Key2; } return true; }, "Key3 must be > than Key2."); // Failure message. services.AddControllersWithViews(); }
或者用于复杂验证的 IValidateOptions
public class MyConfigValidation : IValidateOptions<MyConfigOptions> { public MyConfigOptions _config { get; private set; } public MyConfigValidation(IConfiguration config) { _config = config.GetSection(MyConfigOptions.MyConfig) .Get<MyConfigOptions>(); } public ValidateOptionsResult Validate(string name, MyConfigOptions options) { string vor=null; var rx = new Regex(@"^[a-zA-Z''-'\s]{1,40}$"); var match = rx.Match(options.Key1); if (string.IsNullOrEmpty(match.Value)) { vor = $"{options.Key1} doesn't match RegEx \n"; } if ( options.Key2 < 0 || options.Key2 > 1000) { vor = $"{options.Key2} doesn't match Range 0 - 1000 \n"; } if (_config.Key2 != default) { if(_config.Key3 <= _config.Key2) { vor += "Key3 must be > than Key2."; } } if (vor != null) { return ValidateOptionsResult.Fail(vor); } return ValidateOptionsResult.Success; } }
IValidateOptions
允许将验证代码移出 StartUp
并将其移入类中。
public void ConfigureServices(IServiceCollection services) { services.Configure<MyConfigOptions>(Configuration.GetSection( MyConfigOptions.MyConfig)); services.TryAddEnumerable(ServiceDescriptor.Singleton<IValidateOptions <MyConfigOptions>, MyConfigValidation>()); services.AddControllersWithViews(); }
使用 IPostConfigureOptions<TOptions> 设置后期配置
//进行所有 IConfigureOptions<TOptions> 配置后运行后期配置: services.PostConfigure<MyOptions>(myOptions => { myOptions.Option1 = "post_configured_option1_value"; }); //对命名选项进行后期配置: services.PostConfigure<MyOptions>("named_options_1", myOptions => { myOptions.Option1 = "post_configured_option1_value"; }); //使用 PostConfigureAll 对所有配置实例进行后期配置: services.PostConfigureAll<MyOptions>(myOptions => { myOptions.Option1 = "post_configured_option1_value"; });
安全和机密管理器
请勿在配置提供程序代码或纯文本配置文件中存储密码或其他敏感数据。 机密管理器可用于存储开发环境中的机密。
机密管理器会在 appsettings.json 和 appsettings.Environment
.json 之后读取配置设置 。
环境变量
使用默认配置,EnvironmentVariablesConfigurationProvider 会在读取 appsettings.json、appsettings.Environment
.json 和机密管理器后从环境变量键值对加载配置 。 因此,从环境中读取的键值会替代从 appsettings.json、appsettings.Environment
.json 和机密管理器中读取的值 。
默认配置会加载前缀为 DOTNET_
和 ASPNETCORE_
的环境变量和命令行参数。 DOTNET_
和 ASPNETCORE_
前缀会由 ASP.NET Core 用于主机和应用配置,但不用于用户配置。
在 launchSettings.json 中设置的环境变量
launch.json/launchSettings.json 是用于开发环境的工具配置文件
在 launchSettings.json 中设置的环境变量将替代在系统环境中设置的变量。
JSON 配置提供程序
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("MyConfig.json",
optional: true,
reloadOnChange: true);
})
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
var myKeyValue = Configuration["MyKey"];
var title = Configuration["Position:Title"];
var name = Configuration["Position:Name"];
var defaultLogLevel = Configuration["Logging:LogLevel:Default"];
读取 MyConfig.json 文件之前的默认配置提供程序。 MyConfig.json 文件中的设置会替代默认配置提供程序中的设置,包括环境变量配置提供程序和命令行配置提供程序。
XML 配置提供程序
XmlConfigurationProvider 在运行时从 XML 文件键值对加载配置。
config.AddXmlFile("MyXMLFile.xml", optional: true, reloadOnChange: true) .AddXmlFile($"MyXMLFile.{env.EnvironmentName}.xml", optional: true, reloadOnChange: true);
内存配置提供程序
MemoryConfigurationProvider 使用内存中集合作为配置键值对。
public class Program { public static void Main(string[] args) { CreateHostBuilder(args).Build().Run(); } public static IHostBuilder CreateHostBuilder(string[] args) { var Dict = new Dictionary<string, string> { {"MyKey", "Dictionary MyKey Value"}, {"Position:Title", "Dictionary_Title"}, {"Position:Name", "Dictionary_Name" }, {"Logging:LogLevel:Default", "Warning"} }; return Host.CreateDefaultBuilder(args) .ConfigureAppConfiguration((hostingContext, config) => { config.AddInMemoryCollection(Dict); }) .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); }); } }
配置键和值
配置键:
- 不区分大小写。 例如,
ConnectionString
和connectionstring
被视为等效键。 - 如果在多个配置提供程序中设置了某一键和值,则会使用最后添加的提供程序中的值。 有关详细信息,请参阅默认配置。
- 分层键
- 在配置 API 中,冒号分隔符 (
:
) 适用于所有平台。 - 在环境变量中,冒号分隔符可能无法适用于所有平台。 所有平台均支持采用双下划线
__
,并且它会自动转换为冒号:
。
- 在配置 API 中,冒号分隔符 (
- ConfigurationBinder 支持使用配置键中的数组索引将数组绑定到对象。 数组绑定将在将数组绑定到类部分中进行介绍。
配置值:
- 为字符串。
- NULL 值不能存储在配置中或绑定到对象。
绑定数组
ConfigurationBinder.Bind 支持使用配置键中的数组索引将数组绑定到对象。
{ "array": { "entries": { "0": "value00", "1": "value10", "2": "value20", "4": "value40", "5": "value50" } } } public class ArrayExample { public string[] Entries { get; set; } } public class ArrayModel : PageModel { private readonly IConfiguration Config; public ArrayExample _array { get; private set; } public ArrayModel(IConfiguration config) { Config = config; } public ContentResult OnGet() { _array = Config.GetSection("array").Get<ArrayExample>(); string s = null; for (int j = 0; j < _array.Entries.Length; j++) { s += $"Index: {j} Value: {_array.Entries[j]} \n"; } return Content(s); } }
在前面的输出中,索引 3 具有值 value40
,与 MyArray.json 中的 "4": "value40",
相对应。 绑定的数组索引是连续的,并且未绑定到配置键索引。