使用.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应用(7)——使用AutoMapper实现GET请求
使用.NET 6开发TodoList应用(27)——实现APISwagger文档化
使用.NET 6开发TodoList应用(13)——实现查询分页
使用.NET 6开发TodoList应用(填坑1)——实现当前登录用户获取

热门标签
.NET Core .NET Reactor ag-grid AI发布 api安全 ASP.NET Core C#DLL加密 C#播放声音 C#代码混淆 C#代码加密 ChromeDriver Codex DateTime DBeaver devexpress devTool DLL混淆 edge.js EF EFCore Electron element-ui el-form el-table excel FastReport FileStream FolderBrowerDialog FolderSelectDialog form提交 git gridcontrol gridview input javascript json字符串 JS转换对象JSON jwt JWT授权 linq log Math MCP mitmproxy MVC MySQL Navicat netstat nginx node_modules NSwag Nuget Nuget镜像 number PowerShell pyinstaller python pythoncom python爬虫 python抓包 pywin32 redis Requests-html RestSharp Selenium sql SQL Server Swagger to-cms Visual Studio VSCode vue VueRouter vue路由 VUE页面通讯 Webpack Windows Windows服务 winform wmi xlrd yaml YESCMS YESWEB开发框架 白象 表单提交 播放声音 打开URL 代码混淆 弹窗提醒 端口占用 对象转换 分布式 公共字典 机器码 进程排查 静态资源 开发指南 路由参数 密钥 配置教程 配置文件 权限 人工智能 任务 任务调度 日期间隔 日志 日志记录 省市区 授权验证 数据库 四舍五入 文案 文件读取 文件夹选择 文件目录选择 问题排查 行政区域数据 页面通讯 中间件 CSharp 事务锁 工单系统 并发控制 重复提交 CMS Markdig Markdown markdown-it marked 技术选型 VS Code 开发工具 源代码管理 版本控制 Docker PostgreSQL 时区 部署排查 CMS架构 EF Core 主题系统 二次开发 插件系统 容器 运维命令 镜像清理 Linux NAS 远程挂载 飞牛 fnOS
联系我们
联系电话:15090125178(微信同号)
电子邮箱:garson_zhang@163.com
站长微信二维码
微信二维码