.NET6之MiniAPI(五):选项

选项是配置一个升级版,一般情况下是把一个范围内的配置包装成类型,以供使用,比如下面的RedisSetting,是Redis的配置参数:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*", 
  "RedisSetting": {
    "Host": "127.0.0.1",
    "Port": 6379,
    "Password": "123",
    "ConnectionTimeOut": "10ms"
  }
}

可以采用下面的形式把配置类型 实体注入到容器(因为没有说注入这个知识点,这里可以理解为初始化)中。

var builder = WebApplication.CreateBuilder();
builder.Services.Configure<RedisSetting>(builder.Configuration.GetSection("RedisSetting"));
var app = builder.Build();

选项分为三种:普通选项目IOptions,热更新选项IOptionsSnapshot,监控选项IOptionsMonitor,下面分别说明。

IOptions选项

app.MapGet("/appinfo", (IOptions<RedisSetting> options) =>
{
    return options.Value;
});

Demo结果

b122b9ecb0049e13aa0ba7cb118655c8.png

热加载选项:IOptionsSnapshot

app.MapGet("/snapshot", (IOptionsSnapshot<RedisSetting> options) =>
{
    return options.Value;
});

结果

203d8984cd9d685959eb7ac2755be416.png

更新配置ConnectionTimeOut为15ms,在不重启服务的情况下,再次请求,结果会变成15ms

5a8475a692bf4e94f5a0d721196a7af8.png

监控选项:IOptionsMonitor

app.MapGet("/monitorstart", (IOptionsMonitor<RedisSetting> options) =>
{
    options.OnChange(redisSetting =>
   {
       app.Logger.LogInformation(options.CurrentValue.ToString());     
   });
    return "Ok";
});

调用监控

6a8c75512e64afbadac7a8b2624c1560.png

当把ConnectionTimeOut更新成20ms时,OnChange会被触发,日志会打印出来

8e62cbed20a8c0207413785621cf1f76.png

利用监控特性可以与报警联合起来,当监控到配置有变化时,通知服务相关人知晓,是人为设置,还是被篡改,以便采取措施。

选项命名:

当相同的配置有两组时,选项命名就非常有用了,比如一主一备

"RedisSettings": {
    "Main": {
      "Host": "127.0.0.1",
      "Port": 6379,
      "Password": "123",
      "ConnectionTimeOut": "10ms"
    },
    "Prepare": {
      "Host": "127.0.0.1",
      "Port": 6380,
      "Password": "456",
      "ConnectionTimeOut": "12ms"
    }
  }

实体类可以用静态常量区分开来

public record RedisSetting
{
    public const string Main = "Main";
    public const string Prepare = "Prepare";
    public string? Host { get; set; }
    public int Port { get; set; }
    public string? Password { get; set; }
    public string? ConnectionTimeOut { get; set; }
}

分别注入配置即可

builder.Services.Configure<RedisSetting>(RedisSetting.Main,
                                   builder.Configuration.GetSection("RedisSettings:Main"));
builder.Services.Configure<RedisSetting>(RedisSetting.Prepare,
                                    builder.Configuration.GetSection("RedisSettings:Prepare"));

不过只有IOptionsSnapshot和IOptionsMonitor能通过Get方法来获取命名的配置,IOptions没有实现Get方法

app.MapGet("/snapshotredissetting", (IOptionsSnapshot<RedisSetting> options) =>
{
    return options.Get(RedisSetting.Main);
});
app.MapGet("/monitorstart", (IOptionsMonitor<RedisSetting> options) =>
{
    options.OnChange(redisSetting =>
   {
       app.Logger.LogInformation(options.Get(RedisSetting.Main).ToString());
       app.Logger.LogInformation(options.Get(RedisSetting.Prepare).ToString());
   });
    return options.CurrentValue;
});

另外,为了提高配置数据的安全性,可以给配置选项增加验证,可以通过在配置实体类上增加DataAnnotations来验证,也可以自定义验证

public record RedisSetting
{
    public string? Host { get; set; }
    public int Port { get; set; }
    public string? Password { get; set; }
    [RegularExpression(@"^\d+ms$", ErrorMessage = "格式不正确,必须是ms")]
    public string? ConnectionTimeOut { get; set; }
}

上面的ConnectionTimeOut是DataAnnotations方式,下面是自定义验证模式。

builder.Services.AddOptions<RedisSetting>()
          .Bind(builder.Configuration.GetSection("RedisSetting"))
          .ValidateDataAnnotations()
          .Validate(config =>
          {
              if (config.Port < 1000)
              {
                  return false;
              }
              return true;
          }, "端口不能少于1000");

如果验证更复杂,可以自定义类实现,如下:

public class RedisSettingValidation : IValidateOptions<RedisSetting>
{
    public RedisSetting _config { get; init; }
    public RedisSettingValidation(IConfiguration config)
    {
        _config = config.GetSection("RedisSetting")
            .Get<RedisSetting>();
    }
    public ValidateOptionsResult Validate(string name, RedisSetting options)
    {
        string? vor=null;
        var rx = new Regex(@"^((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)$");
        if (options != null&&options.Host!=null)
        {
            var match = rx.Match(options.Host);
            if (string.IsNullOrEmpty(match.Value))
            {
                vor = $"{options.Host} 格式不正确";
            }
            if (vor != null)
            {
                return ValidateOptionsResult.Fail(vor);
            }
        }
        return ValidateOptionsResult.Success;
    }
}

添加验证类型

builder.Services.Configure<RedisSetting>(builder.Configuration.GetSection("RedisSetting"));
builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton<IValidateOptions
                            <RedisSetting>, RedisSettingValidation>());
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值