• .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结果

     

     

     热加载选项:IOptionsSnapshot

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

    结果

     

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

     

     监控选项:IOptionsMonitor

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

    调用监控

     

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

     

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

    选项命名:

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

      "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>());
     
      想要更快更方便的了解相关知识,可以关注微信公众号 

     

     

     

  • 相关阅读:
    资源放送丨《Oracle存储过程中的性能瓶颈点》PPT&视频
    警示:一个update语句引起大量gc等待和业务卡顿
    周末直播丨细致入微
    Java VS Python:哪个未来发展更好?
    【LeetCode】279.完全平方数(四种方法,不怕不会!开拓思维)
    事件驱动
    Android初级教程以动画的形式弹出窗体
    Android简易实战教程--第五话《开发一键锁屏应用》
    迎战大数据-Oracle篇
    Android初级教程获取手机位置信息GPS与动态获取最佳方式
  • 原文地址:https://www.cnblogs.com/ljknlb/p/16496017.html
Copyright © 2020-2023  润新知