在ASP.NET中,一般情况下都是通过web.config来设置应用程序配置信息,要使用其它方式(比如JSON文件)来进行配置都需要自行扩展。而ASP.NET Core中就丰富的配置的方式,支持的配置方式包括:
- Azure Key Vault
- 命令行参数:dotnet run CommandLineKey1=value1 --CommandLineKey2=value2 /CommandLineKey3=value3
- (已安装或已创建的)自定义提供程序
- 目录文件
- 环境变量
- 内存中的 .NET 对象
- 设置文件(INI,JSON,XML)
本文主要介绍JSON文件配置的使用方式。
默认情况下,使用 Startup.cs的CreateDefaultBuilder 初始化新的 WebHostBuilder 时,会自动加载appsettings.json和appsettings.{Environment}.json配置文件,如果你的配置想要放到其他JSON文件中,使用下面的方法加载配置文件:
public class Program
{
public static void Main(string[] args)
{
CreateWebHostBuilder(args).Build().Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((hostingContext, config) =>
{
config.SetBasePath(Directory.GetCurrentDirectory());
config.AddJsonFile("config.json", optional: true, reloadOnChange: true);
})
.UseStartup<Startup>();
}
1. 读取JSON文件中的配置
假设 appsettings.json 内容如下:
{
"Logging": {
"LogLevel": {
"Default": "Warning"
}
},
"AllowedHosts": "*",
"Starship": {
"name": "USS Enterprise",
"registry": "NCC-1701",
"class": "Constitution",
"length": 304.8,
"commissioned": false
}
}
ASP.NET Core加载配置文件后会展平成如下形式:
键 | 值 |
Logging:LogLevel:Default | Warning |
AllowedHosts | * |
Starship:name | USS Enterprise |
Starship:registry | NCC-1701 |
Starship:class | Constitution |
Starship:length | 304.8 |
Starship:commissioned | false |
要读取这些键值可以用几下几个方法:
- GetValue:读取Key对应的值
Configuration.GetValue<string>("Logging:LogLevel:Default") - GetSection:读取某个配置节点,返回 IConfigurationSection
var configSection = _config.GetSection("Starship");
configSection.GetValue<string>("name"); - Exists:判断配置节点是否存在
Configuration.GetSection("Starship").Exists(); - GetChildren:获取子节点信息
Configuration.GetSection("Starship").GetChildren();
要读取配置节点并绑定到类,使用下面的方法:
public class Starship
{
public string Name { get; set; }
public string Registry { get; set; }
public string Class { get; set; }
public decimal Length { get; set; }
public bool Commissioned { get; set; }
}
var starship = Configuration.GetSection("Starship").Get<Starship>();
2. 自定义配置提供程序
ASP.NET Core中内置了丰富的配置提供程序,可以从内存数据、JSON文件、XML文件、命令行中加载配置。
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((hostingContext, config) =>
{
config.SetBasePath(Directory.GetCurrentDirectory());
config.AddInMemoryCollection(arrayDict);
config.AddJsonFile(
"json_array.json", optional: false, reloadOnChange: false);
config.AddJsonFile(
"starship.json", optional: false, reloadOnChange: false);
config.AddXmlFile(
"tvshow.xml", optional: false, reloadOnChange: false);
config.AddCommandLine(args);
})
.UseStartup<Startup>();
但如果要从Redis或数据库中读取配置怎么办呢?就需要进行扩展了,下面使用 Redis 配置提供程序作为示例:
2.1. 实现 IConfigurationSource 接口
public class RedisConfigurationSource : IConfigurationSource
{
public string RedisString { get; set; }
public string ConfigKey { get; set; }
public int ReloadTime { get; set; }
public RedisConfigurationSource(string redisString, string configKey, int reloadTime)
{
RedisString = redisString;
ConfigKey = configKey;
ReloadTime = reloadTime;
}
public IConfigurationProvider Build(IConfigurationBuilder builder)
{
return new RedisConfigurationProvider(this);
}
}
2.2.继承 ConfigurationProvider 类,从 Redis 读取配置
public class RedisConfigurationProvider : ConfigurationProvider
{
private RedisConfigurationSource _source;
private readonly Task _configurationListeningTask;
public RedisConfigurationProvider(RedisConfigurationSource source)
{
_source = source;
_configurationListeningTask = new Task(ListenToConfigurationChanges);
}
public override void Load()
{
LoadAsync().GetAwaiter();
}
public async Task LoadAsync()
{
Data = await QueryDataAsync();
//启动配置更改监听
if (_configurationListeningTask.Status == TaskStatus.Created)
_configurationListeningTask.Start();
}
private async void ListenToConfigurationChanges()
{
if (_source.ReloadTime <= 0)
return;
while (true)
{
try
{
await Task.Delay(TimeSpan.FromSeconds(_source.ReloadTime));
Data = await QueryDataAsync();
OnReload();
}
catch
{
}
}
}
public async Task<IDictionary<string, string>> QueryDataAsync()
{
var rds = new CSRedis.CSRedisClient(_source.RedisString);
RedisHelper.Initialization(rds);
var tokens = JToken.Parse(await rds.GetAsync(_source.ConfigKey));
return tokens
.Select(k => KeyValuePair.Create
(
k.Value<string>("Key"),
k.Value<string>("Value") != null ? JToken.Parse(Encoding.UTF8.GetString(Convert.FromBase64String(k.Value<string>("Value")))) : null
))
.Where(v => !string.IsNullOrWhiteSpace(v.Key))
.SelectMany(Flatten)
.ToDictionary(v => ConfigurationPath.Combine(v.Key.Split('/')), v => v.Value, StringComparer.OrdinalIgnoreCase);
}
#region 帮助方法
//递归展平JSON节点
private static IEnumerable<KeyValuePair<string, string>> Flatten(KeyValuePair<string, JToken> tuple)
{
if (!(tuple.Value is JObject value))
yield break;
foreach (var property in value)
{
var propertyKey = $"{tuple.Key}/{property.Key}";
switch (property.Value.Type)
{
case JTokenType.Object:
string v = Newtonsoft.Json.JsonConvert.SerializeObject(property.Value);
yield return KeyValuePair.Create(propertyKey, v);
break;
case JTokenType.Array:
break;
default:
yield return KeyValuePair.Create(propertyKey, property.Value.Value<string>());
break;
}
}
}
#endregion
}
2.3.增加 Extensions
public static class RedisExtensions
{
public static IConfigurationBuilder AddRedisConfiguration(
this IConfigurationBuilder builder, string redisString = "127.0.0.1:6379",
string configKey = "web1.config", int reloadTime = 15)
{
return builder.Add(new RedisConfigurationSource(redisString, configKey, reloadTime));
}
}
添加完上面的代码后,就能在 CreateWebHostBuilder()时使用自定义配置提供程序了。
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((hostingContext, config) =>
{
config.AddRedisConfiguration("127.0.0.1:6379", "web1.json", 15);
})
.UseStartup<Startup>();