Ultimate ASP.NET CORE 6.0 Web API --- 读书笔记(29)
29 Binding Configuration And Options Pattern
本文内容来自书籍: Marinko Spasojevic - Ultimate ASP.NET Core Web API - From Zero To Six-Figure Backend Developer (2nd edition)
在之前的章节中,我们的JWT
的配置是放在appsetting.json
这个文件中的,然后需要读取的时候,会使用IConfiguration
接口的方法
var jwtSettings = configuration.GetSection("JwtSettings");
ValidIssuer = jwtSettings["validIssuer"]
当然,这种方法可以很好的正常工作,但是,在获取方式上,我们必须输入key
才能获取到值,而这样是很容易出错并且是在重复地编写类似的代码的。然后冒着风险引入错误到代码中,这会消耗我们大量的时间区排错,而且只有当读取这个值的时候,才会发生这种错误
所以,为了避免这种问题,我们会将配置数据绑定到一个强类型的对象上。
29.1 Binding Configuration
首先,在Entities
中创建ConfigurationModels/JwtConfiguration
public class JwtConfiguration
{
public string Section { get; set; } = "JwtSettings";
public string? ValidIssuer { get; set; }
public string? ValidAudience { get; set; }
public string? Expires { get; set; }
}
然后,在读取JWT
配置的地方,修改读取的方式
public static void ConfigureJWT(this IServiceCollection services, IConfiguration configuration)
{
var jwtConfiguration = new JwtConfiguration();
configuration.Bind(jwtConfiguration.Section, jwtConfiguration);
var secretKey = Environment.GetEnvironmentVariable("SECRET") ?? "asdnAAjhaskdijhciwn";
services.AddAuthentication(opt =>
{
opt.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
opt.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = jwtConfiguration.ValidIssuer,
ValidAudience = jwtConfiguration.ValidAudience,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secretKey))
};
});
}
29.2 Options Pattern
前面展示的将配置参数绑定到一个强类型对象上,而Options Pattern
也可以提供类似的功能,而且还能提供更多的结构化方法和更多特性,比如验证、实时重载,而且更加容易测试
一旦我们将配置类加载数据后,我们可以通过DI
注入一个IOptions<T>
到我们需要配置参数的地方
如果我们需要不停机重新加载配置文件,那我们可以使用IOptionsSnapshot<T>
或者IOptionsMonitor<T>
还有就是它的验证机制是使用的DataAnotations attributes
,也就是我们经常用来验证模型数据的注解
最后是它非常容易测试,因为它提供了helper
方法,可以简单地mock
一个option class
接下来我们开始使用这个IOptions
接口
首先是,我们需要将前面的JwtConfiguration
注册到服务里面
public static void AddJwtConfiguration(this IServiceCollection services, IConfiguration configuration) =>
services.Configure<JwtConfiguration>(configuration.GetSection("JwtSettings"));
builder.Services.AddJwtConfiguration(builder.Configuration);
然后我们就可以使用了
public sealed class ServiceManager : IServiceManager
{
private readonly IMapper _mapper;
private readonly Lazy<ICompanyService> _companyService;
private readonly Lazy<IEmployeeService> _employeeService;
private readonly Lazy<IAuthenticationService> _authenticationService;
public ServiceManager(IRepositoryManager repositoryManager, ILoggerManager logger, IMapper mapper,
UserManager<User> userManager,
IOptions<JwtConfiguration> configuration)
{
_mapper = mapper;
_companyService = new Lazy<ICompanyService>(() => new CompanyService(repositoryManager, logger, mapper));
_employeeService = new Lazy<IEmployeeService>(() => new EmployeeService(repositoryManager, logger, mapper));
_authenticationService =
new Lazy<IAuthenticationService>(
() => new AuthenticationService(logger, mapper, userManager, configuration));
}
public ICompanyService CompanyService => _companyService.Value;
public IEmployeeService EmployeeService => _employeeService.Value;
public IAuthenticationService AuthenticationService => _authenticationService.Value;
}
internal sealed class AuthenticationService : IAuthenticationService
{
private readonly ILoggerManager _logger;
private readonly IMapper _mapper;
private readonly UserManager<User> _userManager;
private readonly IOptions<JwtConfiguration> _configuration;
private readonly JwtConfiguration _jwtConfiguration;
private User? _user;
public AuthenticationService(ILoggerManager logger, IMapper mapper,
UserManager<User> userManager, IOptions<JwtConfiguration> configuration)
{
_logger = logger;
_mapper = mapper;
_userManager = userManager;
_configuration = configuration;
_jwtConfiguration = _configuration.Value;
}
}
如果这个配置,需要在某些时候进行修改,但是有不希望停机的时候,那么可以使用不一样的两种接口来实现
代码上,只需要将IOptions<JwtConfiguration>
替换为IOptionsSnapshot<JwtConfiguration>
或者IOptionsMonitor<JwtConfiguration>
而这两种接口的区别是:IOptionsSnapshot
这个接口会被注册为scoped
的服务,所以它不可以被注入到singleton
类型的服务;而IOptionsMonitor
是会被注册为singleton
服务,它可以被注入到任意生命周期的服务
为了更加清晰的接口间的比较,下面列出来
IOptions
- Options的原始接口,比绑定整个配置更加友好
- 不支持配置热更新
- 注册为
singleton
服务,配置参数的绑定只会发生一次 - 不支持命名选项
IOptionsSnapshot
- 注册为
scoped
服务 - 支持配置热更新
- 不能注入到
singleton
的服务 - 配置参数会在每次请求都重新读取
- 支持命名选项
IOptionsMonitor
- 注册为
singleton
服务 - 支持配置热更新
- 可以注入到任意生命周期的服务
- 配置参数会被缓存而且会马上重新加载
- 支持命名选项
前面提到几次Named Options
,现在解释以下是什么意思,假设现在有配置文件如下
"JwtSettings": {
"validIssuer": "CodeMazeAPI",
"validAudience": "https://localhost:5001",
"expires": 5
},
"JwtAPI2Settings": {
"validIssuer": "CodeMazeAPI2",
"validAudience": "https://localhost:5002",
"expires": 10
}
然后我们增加一个新的类接收新的配置,虽然字段全都一样
services.Configure<JwtConfiguration>("JwtSettings", configuration.GetSection("JwtSettings"));
services.Configure<JwtConfiguration>("JwtAPI2Settings", configuration.GetSection("JwtAPI2Settings"));
字段都一样,只是key不一样,其实我们可以用同一个类来接收,只是名字不一样就可以了
这样,我们在获取配置的时候,就不使用Value
方法,而是用Get
方法
_jwtConfiguration = _configuration.Get("JwtSettings");
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?