zoukankan      html  css  js  c++  java
  • ASP.NET Core 学习笔记 第五篇 ASP.NET Core 中的选项

    前言

    还记得上一篇文章中所说的配置吗?本篇文章算是上一篇的延续吧。在 .NET Core 中读取配置文件大多数会为配置选项绑定一个POCO(Plain Old CLR Object)对象,并通过依赖注入使用它。而这种使用方式称之为选项模式。而选项模式使是用类来提供对相关设置组的强类型访问,同时选项还提供验证配置数据的机制,是不是很强大,让我们一点点揭开它的神秘面纱。

    ASP.NET Core 6.0 Web API简要说明

    首先开发工具上需要VS2022了,这里非常推荐大家下载使用,在编码上真的越来越符合我们开发者的使用了,提示也更加智能化,VS2022详细说明,下载以及注册码可以参考我的上一篇博客,这里就不做详细说明了VS2022传送门(含注册码)
    .NET 6.0 已经出来有一段时间了,为了跟进技术的进步,笔者后续的系列笔记也将使用.NET 6.0作为目标框架,那么.NET 6.0 ASP.NET Core Web API 中带了了哪些变化呢?这里我做下简要说明。

    1. 打开Visual Studio 2022,创建新项目,选择“ASP.NET Core Web API”项目模板:
      image
      这里可以看见Use controllers选项默认选中的,取消该选项,则会创建最小Web API。
    2. 结构变化
      image
      这里可以看下Program.cs文件的变化
    var builder = WebApplication.CreateBuilder(args);
    
    // Add services to the container.
    
    builder.Services.AddControllers();
    
    var app = builder.Build();
    
    // Configure the HTTP request pipeline.
    
    app.UseHttpsRedirection();
    
    app.UseAuthorization();
    
    app.MapControllers();
    
    app.Run();
    
    

    可以发现我们熟知的Startup.cs不见了,现在,全部都在Program.cs中实现:

    • 在WebApplicationBuilder实例上使用Addxxx方法向DI容器注册特定服务,类似Startup类的ConfigureServices方法实现。
    • 在WebApplication实例上使用Usexxx方法将一系列中间件加入到HTTP管道,类似Startup类的Configure方法实现。
    • DTO使用record定义,而且也放在同一个Program.cs文件中。
      说明: Visual Studio 2022默认使用 Kestrel Web服务器,而不是IIS Express。

    配置绑定

    首先创建一个json格式的配置文件

    {
      "JsonConfig": {
        "Title": "MyTitle",
        "Name": "Tom"
      }
    }
    

    创建选项类

        public class MyOptions
        {
            public const string JsonConfig = "JsonConfig";
    
            public string? Title { get; set; }
    
            public string? Name { get; set; }
        }
    

    选项类说明:

    • 必须是包含公共无参数构造函数的非抽象类。
    • 类型的所有公共读写属性都已绑定。
    • 不会绑定字段。 在上面的代码中,Position 未绑定。 由于使用了 Position 属性,因此在将类绑定到配置提供程序时,不需要在应用中对字符串 "Position" 进行硬编码。

    使用 ConfigurationBinder 绑定

    新建一个控制器来读取配置文件。

        [Route("api/[controller]/[action]")]
        [ApiController]
        public class OptionsController : ControllerBase
        {
            private readonly IConfiguration _configuration;
    
            public OptionsController(IConfiguration configuration)
            {
                _configuration=configuration;
            }
    
            public ContentResult GetOptions()
            {
                var options = new MyOptions();
    
                //方式1
                _configuration.GetSection(MyOptions.JsonConfig).Bind(options);
    
                //方式2
                _configuration.GetSection(MyOptions.JsonConfig).Get<MyOptions>();
    
                return Content($"Title:{options.Title}\n Name:{options.Name}");
            }
        }
    

    说明:
    这里的类型绑定有两种方式分别为ConfigurationBinder.BindConfigurationBinder.Get ,
    前者相对于后者更方便一些,在开发过程当中比较推荐使用后者做类型绑定。

    依赖注入绑定

    服务容器中绑定配置。

    using OptionsTest.Model;
    
    var builder = WebApplication.CreateBuilder(args);
    
    // Add services to the container.
    
    builder.Services.AddControllers();
    
    builder.Configuration.AddJsonFile("MyJsonConfig.json", optional: true, reloadOnChange: true);
    
    //注入绑定
    builder.Services.Configure<MyOptions>(builder.Configuration.GetSection(MyOptions.JsonConfig));
    
    var app = builder.Build();
    
    // Configure the HTTP request pipeline.
    
    app.UseAuthorization();
    
    app.MapControllers();
    
    app.Run();
    

    新建一个控制器来读取配置文件。

        [Route("api/[controller]/[action]")]
        [ApiController]
        public class Options2Controller : ControllerBase
        {
            private readonly MyOptions _options;
    
            public Options2Controller(IOptions<MyOptions> options)
            {
                _options = options.Value;
            }
    
            public ContentResult GetOptions()
            {
                return Content($"Title:{_options.Title}\n Name:{_options.Name}");
            }
        }
    

    选项接口

    IOptions:

    • 不支持:
      • 在应用启动后读取配置数据。
      • 命名选项
      • 注册为单一实例且可以注入到任何服务生存期。

    IOptionsSnapshot:

    • 在每次请求时应重新计算选项的方案中有用。 有关详细信息,请参阅使用 IOptionsSnapshot 读取已更新的数据。
    • 注册为范围内,因此无法注入到单一实例服务。
    • 支持命名选项

    IOptionsMonitor:

    • 用于检索选项并管理 TOptions 实例的选项通知。
    • 注册为单一实例且可以注入到任何服务生存期。
      • 支持:
      • 更改通知
      • 命名选项
      • 可重载配置
      • 选择性选项失效 (IOptionsMonitorCache)

    IOptionsSnapshot

    在项目中如何在改变配置文件后,在不重启项目的前提下自动加载配置文件呢?IOptionsSnapshot就支持读取已更新的配置值。
    在前面项目的基础上略做改变就能做到,MyOptions.cs、MyJsonConfig.json、Program.cs不需要改变。在OptionsController中改用IOptionsSnapshot注入就可以了,代码如下:

        [Route("api/[controller]/[action]")]
        [ApiController]
        public class Options2Controller : ControllerBase
        {
            private readonly MyOptions _options;
    
            public Options2Controller(IOptionsSnapshot<MyOptions> options)
            {
                _options = options.Value;
            }
    
            public ContentResult GetOptions()
            {
                return Content($"Title:{_options.Title}\n Name:{_options.Name}");
            }
        }
    

    是不是很简单,这样每次改完配置文件就不需要重启程序了。

    IOptionsMonitor

    MyOptions.cs、MyJsonConfig.json、Program.cs不需要改变。在OptionsController中改用IOptionsMonitor注入就可以了,代码如下:

        [Route("api/[controller]/[action]")]
        [ApiController]
        public class OptionsController : ControllerBase
        {
            private readonly MyOptions _options;
    
            public Options2Controller(IOptionsMonitor<MyOptions> options)
            {
                _options = options.Value;
            }
    
            public ContentResult GetOptions()
            {
                return Content($"Title:{_options.Title}\n Name:{_options.Name}");
            }
        }
    

    说明:

    • IOptionsMonitor 是一种单一示例服务,可随时检索当前选项值,这在单一实例依赖项中尤其有用。
    • IOptionsSnapshot 是一种作用域服务,并在构造 IOptionsSnapshot 对象时提供选项的快照。 选项快照旨在用于暂时性和有作用域的依赖项。

    命名选项

    命名选项:

    • 当多个配置节绑定到同一属性时有用。
    • 区分大小写。
      新建json配置文件
    {
      "JsonConfig": {
        "Title": "MyTitle",
        "Name": "Tom"
      },
      "JsonConfig2": {
        "Title": "MyTitle2",
        "Name": "Jerry"
      }
    }
    

    在属性相同的情况下,不需要创建两个类,我们增加一个字段就可以了。

        public class MyOptions
        {
            public const string JsonConfig = "JsonConfig";
    
            public const string JsonConfig2 = "JsonConfig2";
    
            public string? Title { get; set; }
    
            public string? Name { get; set; }
        }
    

    Program.cs中配置命名选项。

    using OptionsTest3.Model;
    
    var builder = WebApplication.CreateBuilder(args);
    
    // Add services to the container.
    
    builder.Services.AddControllers();
    builder.Configuration.AddJsonFile("MyJsonConfig.json", optional: true, reloadOnChange: true);
    
    //注入绑定
    builder.Services.Configure<MyOptions>(MyOptions.JsonConfig,builder.Configuration.GetSection(MyOptions.JsonConfig));
    builder.Services.Configure<MyOptions>(MyOptions.JsonConfig2, builder.Configuration.GetSection(MyOptions.JsonConfig2));
    var app = builder.Build();
    
    
    // Configure the HTTP request pipeline.
    
    app.UseAuthorization();
    
    app.MapControllers();
    
    app.Run();
    

    在Controller中读取配置。

        [Route("api/[controller]/[action]")]
        [ApiController]
        public class Options3Controller : ControllerBase
        {
            private readonly MyOptions _options;
    
            private readonly MyOptions _options2;
    
            public Options3Controller(IOptionsSnapshot<MyOptions> options)
            {
                _options = options.Get(MyOptions.JsonConfig);
    
                _options2 = options.Get(MyOptions.JsonConfig2);
            }
    
            public ContentResult GetOptions()
            {
                return Content($"Title:{_options.Title}\n Name:{_options.Name}\n" +
                               $"Title:{_options2.Title}\n Name:{_options2.Name}");
            }
        }
    

    选项验证

    在配置文件中对于配置项的验证是必不可少的。这样可以保证程序配置的正确性。具体如何实现呢,且听笔者慢慢道来。
    首先创建一个简单的Json配置文件。

    {
      "JsonConfig": {
        "ID": 10000000,
        "Title": "MyTitle",
        "Name": "Tom"
      }
    }
    

    创建我们配置项的验证Model。

        public class MyOptions
        {
            public const string JsonConfig = "JsonConfig";
    
            [Range(0, 1000,
            ErrorMessage = "值 {0}必须在 {1} 和 {2}之间。")]
            public int ID { get; set; }
    
            [RegularExpression(@"^[a-zA-Z''-'\s]{1,40}$")]
            public string? Title { get; set; }
    
            public string? Name { get; set; }
        }
    

    在Program.cs中配置我们的选项和验证。

    using OptionValidtate.Model;
    using System.Configuration;
    
    var builder = WebApplication.CreateBuilder(args);
    
    // Add services to the container.
    
    builder.Services.AddControllers();
    builder.Configuration.AddJsonFile("MyJsonConfig.json", optional: true, reloadOnChange: true);
    builder.Services.AddOptions<MyOptions>().Bind(builder.Configuration.GetSection(MyOptions.JsonConfig)).ValidateDataAnnotations();
    
    
    var app = builder.Build();
    
    // Configure the HTTP request pipeline.
    
    app.UseAuthorization();
    
    app.MapControllers();
    
    app.Run();
    

    说明:注意到和之前的代码有什么不同了吗?在我们正是通过调用ValidateDataAnnotations 以使用 DataAnnotations 来完成验证。
    在我们的Controller中读取我们的配置。这里用到了ILogger,可以先不用关心,在后续笔者会详细讲解。

        [Route("api/[controller]/[action]")]
        [ApiController]
        public class OptionsController : ControllerBase
        {
            private readonly ILogger<OptionsController> _logger;
            private readonly IOptions<MyOptions> _options;
    
            public OptionsController(IOptions<MyOptions> options, ILogger<OptionsController> logger)
            {
                _options = options;
                _logger = logger;
                try
                {
                   var optionsValue = _options.Value;
                }
                catch (OptionsValidationException e)
                {
                    foreach (var failure in e.Failures)
                    {
                        _logger.LogError(failure);
                    }
                }
            }
    
            public ContentResult GetOptions()
            {
                string msg;
    
                try
                {
                    msg=$"ID:{_options.Value.ID}\nTitle:{_options.Value.Title}\nName:{_options.Value.Name}\n";
                }
                catch (OptionsValidationException e)
                {
                    return Content(e.Message);
    
                }
    
                return Content(msg);
            }
        }
    

    还记得咱们配置的ID验证规则吗?启动程序看下我们的验证已经生效了。
    image

    IValidateOptions复杂验证的实现

    如果上面的验证还不满足要求的话,没关系我对上面的代码略作修改,通过IValidateOptions就可以实现更加的复杂验证。
    新建MyValidateOptions类继承IValidateOptions接口并实现。

        public class MyValidateOptions : IValidateOptions<MyOptions>
        {
              public MyOptions _options { get; private set; }
    
            public MyValidateOptions(IConfiguration config)
            {
                _options = config.GetSection(MyOptions.JsonConfig).Get<MyOptions>();
            }
            public ValidateOptionsResult Validate(string name, MyOptions options)
            {
                string msg = null;
                var rxValidate = new Regex(@"^[a-zA-Z''-'\s]{1,40}$");
                if (options.ID<0||options.ID>1000)
                {
                    msg = $"{options.ID}必须在0-1000之间。";
                }
                if (string.IsNullOrEmpty(options.Title))
                {
                    msg = $"{options.Title}必须符合正则要求。";
                }
                if (msg !=null)
                {
                    return ValidateOptionsResult.Fail(msg);
                }
    
                return ValidateOptionsResult.Success;
            }
        }
    

    在Program.cs中配置我们的选项和验证。

    using Microsoft.Extensions.Options;
    using OptionValidtate.Model;
    using System.Configuration;
    
    var builder = WebApplication.CreateBuilder(args);
    
    // Add services to the container.
    
    builder.Services.AddControllers();
    builder.Configuration.AddJsonFile("MyJsonConfig.json", optional: true, reloadOnChange: true);
    builder.Services.AddOptions<MyOptions>().Bind(builder.Configuration.GetSection(MyOptions.JsonConfig));
    builder.Services.AddSingleton<IValidateOptions<MyOptions>,MyValidateOptions>();
    
    var app = builder.Build();
    
    // Configure the HTTP request pipeline.
    
    app.UseAuthorization();
    
    app.MapControllers();
    
    app.Run();
    

    好了配置完毕,运行程序可以看到我们配置的验证已经生效了。

    image

  • 相关阅读:
    Magento速度优化
    magento-connect-manage出现404或者500内部错误的解决办法
    magento -- 给后台分类管理页的分类商品加一栏商品类型
    magento -- 添加新产品时状态默认为激活,库存状态默认为有库存
    magento -- 如何为商品分类(category)添加自定义属性
    magento -- 如何改善前台图片质量
    安装Let's Encrypt SSL证书
    centos版本查看
    ps
    设置桥接虚拟网卡
  • 原文地址:https://www.cnblogs.com/gurenyumao/p/15562034.html
Copyright © 2011-2022 走看看