使用.NET 6开发TodoList应用(26)——实现Configuration和Option的强类型绑定


系列导航及源代码

需求

在上一篇文章使用.NET 6开发TodoList应用(25)——实现RefreshToken中,我们通过使用Configuration获取方法GetSection拿到写在appsettings.Development.json中JWT的相关配置字段,这样实现没有问题,但是我们有更好的选择:通过使用强类型的Configuration绑定方法,或者通过Options相关方法来实现。在本文中我们将会分别来看一下这两种方法的实现。

目标

实现配置字段的强类型绑定,分别通过Configuration绑定和Options实现。

原理与思路

要实现强类型绑定,首先我们需要定义这个配置类型。然后根据需求,选择使用Configuration绑定实现或者使用Options配置实现。二者实现的功能上有一些区别:使用Options模式提供了更多的功能,如校验、热加载,也更方便进行测试。

实现

定义配置类型

根据我们在appsettings.Development.json中的配置:

"JwtSettings": {
  "validIssuer": "TodoListApi",
  "validAudience": "https://localhost:5050",
  "expires": 5
}

Application/Configurations中添加JwtConfiguration类如下:

  • JwtConfiguration.cs
namespace TodoList.Application.Common.Configurations;

public class JwtConfiguration
{
    public string Section { get; set; } = "JwtSettings";
    public string? ValidIssuer { get; set; }
    public string? ValidAudience { get; set; }
    public string? Expires { get; set; }
}

方法1: 通过Configuration绑定实现

修改Infrastructure项目中的DependencyInjection添加认证方法的逻辑:

  • DependencyInjection.cs
// 省略其他...
// 添加认证方法为JWT Token认证
var jwtConfiguration = new JwtConfiguration();
configuration.Bind(jwtConfiguration.Section, jwtConfiguration);

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(Environment.GetEnvironmentVariable("SECRET") ?? "TodoListApiSecretKey"))
        };
    });

修改IdentityService中的逻辑,添加一个私有字段

  • IdentityService.cs
// 添加JWT配置类字段
private readonly JwtConfiguration _jwtConfiguration;

// 构造函数中进行初始化
// 初始化配置对象
_jwtConfiguration = new JwtConfiguration();
configuration.Bind(_jwtConfiguration.Section, _jwtConfiguration);

并将所有之前使用json对象获取字段值的地方都修改成通过私有字段的成员变量获取:

// 省略其他...
ValidIssuer = _jwtConfiguration.ValidIssuer,
ValidAudience = _jwtConfiguration.ValidAudience

验证如下,我们还是通过获取refresh token来检查配置是否成功:
image

看起来没什么问题。下面我们看第二种方法,也是相对比较推荐的做法。

方法2: 通过IOptions配置实现

我们在Infrastructure/DependencyInjection.cs中添加使用IOptions的配置:

  • DependencyInjection.cs
// 使用IOptions配置
services.Configure<JwtConfiguration>(configuration.GetSection("JwtSettings"));

然后通过依赖注入的方式去修改IdentityService

  • IdentityService.cs
public IdentityService(
    ILogger<IdentityService> logger,
    IConfiguration configuration,
    UserManager<ApplicationUser> userManager,
    IOptions<JwtConfiguration> jwtOptions)
{
    _logger = logger;
    _userManager = userManager;
    // 初始化配置对象
    _jwtConfiguration = jwtOptions.Value;
}

其他的不需要再进行修改。下面来验证一下效果,验证方法和刚才一致:
image
可以看到依然是没有问题的。

一点扩展

扩展1: 关于配置热加载

综合我们刚才提到的和所演示的可以看到,我们并没有演示关于配置热加载的功能,如果在程序的运行过程中,我们希望配置文件的改动能够直接反映到应用中,不需要重启应用,可以预想到这个功能还是很重要的。

这个功能是通过IOptionsSnapshot或者IOptionsMonitor来实现的,我们所需要做的就是在依赖注入的时候使用IOptionsSnapshot<JwtConfiguration>或者IOptionsMonitor<JwtConfiguration>代替我们之前使用的IOptions<JwtConfiguration>。在替换的过程中之需要注意以下两点即可:

  1. IOptionsSnapshot本身是注册为ScopedService,所以不能注入到SingletonService中使用;
  2. IOptionsMonitor本身注册为了SingletonService,所以可以注入到SingletonService中使用,但是在取值的时候不是使用Value而是使用CurrentValue

我们使用IOptionsMonitor来举例子验证,只需要修改IdentityService中构造函数的注入部分:

  • IdentityService.cs
public IdentityService(
    ILogger<IdentityService> logger,
    UserManager<ApplicationUser> userManager,
    IOptionsMonitor<JwtConfiguration> jwtOptions)
{
    _logger = logger;
    _userManager = userManager;
    // 使用IOptionsMonitor加载配置
    _jwtConfiguration = jwtOptions.CurrentValue;
}

重新运行项目,我们先直接请求Token:
image

解析出的payload如下,过期时间是之前设置的5分钟后:
image

在不重启应用的情况下,我们去修改appsettings.Development.json中关于过期时间的配置,将过期时间设置为10分钟:

"JwtSettings": {
  "validIssuer": "TodoListApi",
  "validAudience": "https://localhost:5050",
  "expires": 10
}

再次执行获取Token的请求,查看Header里的Date字段值:
image

并把token解析:
image

这个过期时间已经变成10分钟后了,大家可以自己动手试一下。

扩展2: 关于相同类型的多个配置Section处理

有一种情况是在appsettings.Development.json中我们可能会做这样的配置:

"JwtSettings": {
  "validIssuer": "TodoListApi",
  "validAudience": "https://localhost:5050",
  "expires": 5
},
"JwtApiV2Settings": {
  "validIssuer": "TodoListApiV2",
  "validAudience": "https://localhost:5050",
  "expires": 10
}

面对这种情况,我们可以在进行IOptions配置时指定配置名称,像这样:

// 使用IOptions配置
services.Configure<JwtConfiguration>("JwtSettings", configuration.GetSection("JwtSettings"));
services.Configure<JwtConfiguration>("JwtApiV2Settings", configuration.GetSection("JwtApiV2Settings"));

而在需要注入使用的地方也指定对应要进行配置的名称即可:

// 使用IOptionsMonitor加载配置
_jwtConfiguration = jwtOptions.Get("JwtApiV2Settings");

这样就可以正确地使用相应的配置了,就不再继续演示了。

总结

关于三种IOptions的对比见下表:

类型依赖注入类型是否支持配置热加载配置加载更新时机是否支持名称配置
IOptions<T>Singleton注入只在程序运行开始时绑定一次,以后每次获取的都是相同值
IOptionsSnapshot<T>Scoped注入每次请求时都会重新加载配置
IOptionsMonitor<T>Singleton注入配置的值被缓存起来了,当原始配置发生变化时立即发生更新

在本文中我们介绍了如何使用强类型绑定配置项,以及如何实现配置的热加载。对于没有涉及到的诸如配置项的校验等内容(可以通过Annotation实现校验)可以参考官方文档:Options validation

参考资料

  1. Options validation
文章来源:https://www.cnblogs.com/code4nothing/p/build-todolist-26.html

版权声明:本文为YES开发框架网发布内容,转载请附上原文出处连接
管理员
上一篇:记一次 .NET 某消防物联网 后台服务 内存泄漏分析
下一篇:test
评论列表

发表评论

评论内容
昵称:
关联文章

使用.NET 6开发TodoList应用(26)——实现ConfigurationOption类型
使用.NET 6开发TodoList应用(19)——处理OPTIONHEAD请求
使用.NET 6开发TodoList应用(31)——实现基于Github ActionsACICI/CD
使用.NET 6开发TodoList应用(22)——实现缓存
使用.NET 6开发TodoList应用(24)——实现基于JWTIdentity功能
使用.NET 6开发TodoList应用(11)——使用FluentValidationMediatR实现接口请求验证
使用.NET 6开发TodoList应用(12)——实现ActionFilter
使用.NET 6开发TodoList应用(14)——实现查询过滤
使用.NET 6开发TodoList应用(6)——使用MediatR实现POST请求
使用.NET 6开发TodoList应用(15)——实现查询搜索
使用.NET 6开发TodoList应用(9)——实现PUT请求
使用.NET 6开发TodoList应用(25)——实现RefreshToken
使用.NET 6开发TodoList应用(30)——实现Docker打包部署
使用.NET 6开发TodoList应用(23)——实现请求限流
使用.NET 6开发TodoList应用(16)——实现查询排序
使用.NET 6开发TodoList应用(10)——实现DELETE请求以及HTTP请求幂等性
使用.NET 6开发TodoList应用(27)——实现APISwagger文档化
使用.NET 6开发TodoList应用(7)——使用AutoMapper实现GET请求
使用.NET 6开发TodoList应用(13)——实现查询分页
使用.NET 6开发TodoList应用(填坑1)——实现当前登录用户获取

联系我们
联系电话:15090125178(微信同号)
电子邮箱:garson_zhang@163.com
站长微信二维码
微信二维码