使用 .Net 6 在 .Net 核心 Web 应用程序中动态读取 Azure 应用程序配置
Dynamically Reading Azure App Configuration in .Net core Web App using .Net 6
我正在尝试做的事情:我正在尝试使用 .Net 6 web API 应用程序设置 Azure 应用程序配置,并在 Azure 应用程序配置中使用哨兵密钥,目标是能够在 azure 中更改密钥,并且 none 的密钥将在我的应用程序中更新,直到标记值发生更改。理论上,这应该允许我安全地热插拔配置。
我的问题是:当我执行此操作时,IOptionsSnapshot 无法从 Azure 应用程序配置中获取值。我得到的值是空的,即使是第一次。
当我们使用 IConfiguration 时,我第一次可以获得值。
我引用的文档: 我引用了来自 docs.microsoft
的文档
- https://docs.microsoft.com/en-us/azure/azure-app-configuration/quickstart-aspnet-core-app?tabs=core6x
- https://docs.microsoft.com/en-us/azure/azure-app-configuration/enable-dynamic-configuration-aspnet-core?tabs=core5x
对于动态更新,没有可用于 .Net 6 的参考,所以我在 .Net 5 中验证并更新了 .Net 6 的代码
我的环境: 使用来自 Visual Studio Enterprise 2022 的 运行 的 dot net 6,以及 Microsoft.Azure.AppConfiguration.AspNetCore[= 的最新 nuget 包17=]
我的代码:
Program.cs 文件
using ReadingConfiguration.Model;
var builder = WebApplication.CreateBuilder(args);
#region Start reading AppSettings.Json file
//Reading appsettings.json Configuration file using
builder.Services.Configure<MySettingsConfiguration>(
builder.Configuration.GetSection("MySettings"));
builder.Services.AddConfig(builder.Configuration);
#endregion
// Add services to the container.
#region Code start for connecting the Azure App Configuration
// Using to connect the azure App configuration
var connectionString = builder.Configuration.GetConnectionString("AppConfig");
builder.Host.ConfigureAppConfiguration((hostingContext, config) =>
{
var settings = config.Build();
config.AddAzureAppConfiguration(option =>
{
option.Connect(connectionString).ConfigureRefresh(refresh =>
{
refresh.Register("AAConfiguration:Sentinel", refreshAll:true).SetCacheExpiration(new TimeSpan(0, 0, 30));
});
});
})
.ConfigureServices(service =>
{ service.AddControllers();
});
//Middleware for refreshing the azure App configuration
builder.Services.Configure<AAConfiguration>(builder.Configuration.GetSection("AAConfiguration"));
builder.Services.AddAzureAppConfiguration();
builder.Services.AddControllers();
#endregion
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
// Configure the HTTP request pipeline.
// If statement can be removed if we need the swagger only in development
// if (app.Environment.IsDevelopment())
// {
app.UseSwagger();
app.UseSwaggerUI();
// }
//Middleware for refreshing the azure App configuration
app.UseAzureAppConfiguration();
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
正在读取 AzureAppConfigurationController 文件
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Options;
using ReadingConfiguration.Model;
namespace ReadingConfiguration
{
[Route("api/[controller]")]
[ApiController]
public class ReadingAzureAppConfigurationController : ControllerBase
{
private readonly AAConfiguration _aaConfiguration;
private readonly IConfiguration _configuration;
public ReadingAzureAppConfigurationController(IOptionsSnapshot<AAConfiguration> optionsSnapshot,IConfiguration configuration)
{
_aaConfiguration = optionsSnapshot.Value;
_configuration = configuration;
}
[HttpGet]
public string ReadingDynamicAzureAppConfiguration()
{
return _aaConfiguration.Message;
}
[HttpGet]
[Route("ReadingAppConfig")]
public string ReadingAzureAppConfiguration()
{
return _configuration["Message"];
}
}
}
AA配置文件
namespace ReadingConfiguration.Model
{
public class AAConfiguration
{
public string? Message { get; set; }
public int Sentinel { get; set; }
}
}
Appsettings.Json
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"MySettings": {
"Log": true,
"ConnectionStringId": "Default",
"Parameters": {
"IsProduction": false
}
},
"Trading": {
"ChartType": "Monthly",
"Function": "Pivot",
"RSI": true
},
"Car": {
"Manufacturer": "Fiat",
"Model": "Punto",
"Year": 2013
},
"AllowedHosts": "*"
}
MyConfigServiceCollectionExtensions 文件
using Microsoft.Extensions.Configuration;
using ReadingConfiguration.Model;
namespace Microsoft.Extensions.DependencyInjection
{
public static class MyConfigServiceCollectionExtensions
{
public static IServiceCollection AddConfig(this IServiceCollection services, IConfiguration configuration)
{
#region This will read the configuration from appsettings.json
services.Configure<TradingConfiguration>(
configuration.GetSection("Trading")
);
services.Configure<CarConfiguration>(
configuration.GetSection("Car")
);
#endregion
// This will read the configuration azure app configuration
services.Configure<AAConfiguration>(
configuration.GetSection("AAConfiguration")
);
return services;
}
}
}
使用用户机密从本地计算机连接到应用程序配置。
创建秘密管理器密钥以在开发时连接到本地计算机中的 Azure 应用程序配置
dotnet 用户机密 init
在 Secret Manager 中配置 Azure App 配置的连接字符串。
dotnet user-secrets set ConnectionStrings:AppConfig“使用您的应用配置主连接字符串”
- 从 Nuget 添加包 Microsoft.Azure.AppConfiguration.AspNetCore
请参考我的代码示例,它在我身边工作。我创建了一个新的 .net 6 api 项目。 运行 应用程序后,您可以更改 azure 门户中的值以测试刷新。
将连接字符串添加到 appsetting.json:
"ConnectionStrings": {
"AppConfig": "Endpoint=https://xxxx.azconfig.io;Id=yaFxxSgH;Secret=5MYxxxs="
}
我的program.cs,请不要忘记添加服务和中间件,中间件用于监控sentinel key。
using WebApiNet6AzureAppConfig.Models;
var builder = WebApplication.CreateBuilder(args);
ConfigurationManager configuration = builder.Configuration;
var connectionString = builder.Configuration.GetConnectionString("AppConfig");
builder.Host.ConfigureAppConfiguration((hostingContext, config) =>
{
var settings = config.Build();
config.AddAzureAppConfiguration(options =>
{
options.Connect(connectionString)
.ConfigureRefresh(refresh =>
{
refresh.Register("TestApp:Settings:FontColor", refreshAll: true)
.SetCacheExpiration(new TimeSpan(0, 0, 30));
});
});
}).ConfigureServices(services =>
{
services.AddControllers();
});
builder.Services.Configure<AppSettings>(configuration.GetSection("TestApp:Settings"));
builder.Services.AddAzureAppConfiguration();
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseAzureAppConfiguration();
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
我的测试api:
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using WebApiNet6AzureAppConfig.Models;
namespace WebApiNet6AzureAppConfig.Controllers
{
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
private readonly AppSettings _settings;
public WeatherForecastController(IOptionsSnapshot<AppSettings> settings)
{
_settings = settings.Value;
}
[HttpGet(Name = "GetWeatherForecast")]
public string Get()
{
var res = _settings.Message;
return res;
}
}
}
我正在尝试做的事情:我正在尝试使用 .Net 6 web API 应用程序设置 Azure 应用程序配置,并在 Azure 应用程序配置中使用哨兵密钥,目标是能够在 azure 中更改密钥,并且 none 的密钥将在我的应用程序中更新,直到标记值发生更改。理论上,这应该允许我安全地热插拔配置。
我的问题是:当我执行此操作时,IOptionsSnapshot 无法从 Azure 应用程序配置中获取值。我得到的值是空的,即使是第一次。 当我们使用 IConfiguration 时,我第一次可以获得值。
我引用的文档: 我引用了来自 docs.microsoft
的文档- https://docs.microsoft.com/en-us/azure/azure-app-configuration/quickstart-aspnet-core-app?tabs=core6x
- https://docs.microsoft.com/en-us/azure/azure-app-configuration/enable-dynamic-configuration-aspnet-core?tabs=core5x
对于动态更新,没有可用于 .Net 6 的参考,所以我在 .Net 5 中验证并更新了 .Net 6 的代码
我的环境: 使用来自 Visual Studio Enterprise 2022 的 运行 的 dot net 6,以及 Microsoft.Azure.AppConfiguration.AspNetCore[= 的最新 nuget 包17=]
我的代码: Program.cs 文件
using ReadingConfiguration.Model;
var builder = WebApplication.CreateBuilder(args);
#region Start reading AppSettings.Json file
//Reading appsettings.json Configuration file using
builder.Services.Configure<MySettingsConfiguration>(
builder.Configuration.GetSection("MySettings"));
builder.Services.AddConfig(builder.Configuration);
#endregion
// Add services to the container.
#region Code start for connecting the Azure App Configuration
// Using to connect the azure App configuration
var connectionString = builder.Configuration.GetConnectionString("AppConfig");
builder.Host.ConfigureAppConfiguration((hostingContext, config) =>
{
var settings = config.Build();
config.AddAzureAppConfiguration(option =>
{
option.Connect(connectionString).ConfigureRefresh(refresh =>
{
refresh.Register("AAConfiguration:Sentinel", refreshAll:true).SetCacheExpiration(new TimeSpan(0, 0, 30));
});
});
})
.ConfigureServices(service =>
{ service.AddControllers();
});
//Middleware for refreshing the azure App configuration
builder.Services.Configure<AAConfiguration>(builder.Configuration.GetSection("AAConfiguration"));
builder.Services.AddAzureAppConfiguration();
builder.Services.AddControllers();
#endregion
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
// Configure the HTTP request pipeline.
// If statement can be removed if we need the swagger only in development
// if (app.Environment.IsDevelopment())
// {
app.UseSwagger();
app.UseSwaggerUI();
// }
//Middleware for refreshing the azure App configuration
app.UseAzureAppConfiguration();
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
正在读取 AzureAppConfigurationController 文件
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Options;
using ReadingConfiguration.Model;
namespace ReadingConfiguration
{
[Route("api/[controller]")]
[ApiController]
public class ReadingAzureAppConfigurationController : ControllerBase
{
private readonly AAConfiguration _aaConfiguration;
private readonly IConfiguration _configuration;
public ReadingAzureAppConfigurationController(IOptionsSnapshot<AAConfiguration> optionsSnapshot,IConfiguration configuration)
{
_aaConfiguration = optionsSnapshot.Value;
_configuration = configuration;
}
[HttpGet]
public string ReadingDynamicAzureAppConfiguration()
{
return _aaConfiguration.Message;
}
[HttpGet]
[Route("ReadingAppConfig")]
public string ReadingAzureAppConfiguration()
{
return _configuration["Message"];
}
}
}
AA配置文件
namespace ReadingConfiguration.Model
{
public class AAConfiguration
{
public string? Message { get; set; }
public int Sentinel { get; set; }
}
}
Appsettings.Json
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"MySettings": {
"Log": true,
"ConnectionStringId": "Default",
"Parameters": {
"IsProduction": false
}
},
"Trading": {
"ChartType": "Monthly",
"Function": "Pivot",
"RSI": true
},
"Car": {
"Manufacturer": "Fiat",
"Model": "Punto",
"Year": 2013
},
"AllowedHosts": "*"
}
MyConfigServiceCollectionExtensions 文件
using Microsoft.Extensions.Configuration;
using ReadingConfiguration.Model;
namespace Microsoft.Extensions.DependencyInjection
{
public static class MyConfigServiceCollectionExtensions
{
public static IServiceCollection AddConfig(this IServiceCollection services, IConfiguration configuration)
{
#region This will read the configuration from appsettings.json
services.Configure<TradingConfiguration>(
configuration.GetSection("Trading")
);
services.Configure<CarConfiguration>(
configuration.GetSection("Car")
);
#endregion
// This will read the configuration azure app configuration
services.Configure<AAConfiguration>(
configuration.GetSection("AAConfiguration")
);
return services;
}
}
}
使用用户机密从本地计算机连接到应用程序配置。
创建秘密管理器密钥以在开发时连接到本地计算机中的 Azure 应用程序配置 dotnet 用户机密 init
在 Secret Manager 中配置 Azure App 配置的连接字符串。
dotnet user-secrets set ConnectionStrings:AppConfig“使用您的应用配置主连接字符串”
- 从 Nuget 添加包 Microsoft.Azure.AppConfiguration.AspNetCore
请参考我的代码示例,它在我身边工作。我创建了一个新的 .net 6 api 项目。 运行 应用程序后,您可以更改 azure 门户中的值以测试刷新。
将连接字符串添加到 appsetting.json:
"ConnectionStrings": {
"AppConfig": "Endpoint=https://xxxx.azconfig.io;Id=yaFxxSgH;Secret=5MYxxxs="
}
我的program.cs,请不要忘记添加服务和中间件,中间件用于监控sentinel key。
using WebApiNet6AzureAppConfig.Models;
var builder = WebApplication.CreateBuilder(args);
ConfigurationManager configuration = builder.Configuration;
var connectionString = builder.Configuration.GetConnectionString("AppConfig");
builder.Host.ConfigureAppConfiguration((hostingContext, config) =>
{
var settings = config.Build();
config.AddAzureAppConfiguration(options =>
{
options.Connect(connectionString)
.ConfigureRefresh(refresh =>
{
refresh.Register("TestApp:Settings:FontColor", refreshAll: true)
.SetCacheExpiration(new TimeSpan(0, 0, 30));
});
});
}).ConfigureServices(services =>
{
services.AddControllers();
});
builder.Services.Configure<AppSettings>(configuration.GetSection("TestApp:Settings"));
builder.Services.AddAzureAppConfiguration();
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseAzureAppConfiguration();
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
我的测试api:
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using WebApiNet6AzureAppConfig.Models;
namespace WebApiNet6AzureAppConfig.Controllers
{
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
private readonly AppSettings _settings;
public WeatherForecastController(IOptionsSnapshot<AppSettings> settings)
{
_settings = settings.Value;
}
[HttpGet(Name = "GetWeatherForecast")]
public string Get()
{
var res = _settings.Message;
return res;
}
}
}