# 模块二 基础巩固 配置

## 2.2.3 核心模块--配置 <a href="#id-223-he-xin-mo-kuai-pei-zhi" id="id-223-he-xin-mo-kuai-pei-zhi"></a>

* IConfiguration
* Options

ASP.NET Core 中的配置：<https://docs.microsoft.com/zh-cn/aspnet/core/fundamentals/configuration/?view=aspnetcore-5.0>

### IConfiguration <a href="#iconfiguration" id="iconfiguration"></a>

* IConfiguration 的使用
* 层级对象配置到 key-value 键值对转换
* 通过环境变量修改日志级别
* 通过命令行修改日志级别

#### IConfiguration 的使用 <a href="#iconfiguration-de-shi-yong" id="iconfiguration-de-shi-yong"></a>

appsettings.json

```
{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*"
}
```

新增 ConfigController.cs

```
namespace HelloApi.Controllers
{
    [ApiController]
    [Route("[controller]")]
    public class ConfigController : Controller
    {
        private readonly IConfiguration _configuration;

        public ConfigController(IConfiguration configuration)
        {
            _configuration = configuration;
        }

        [HttpGet]
        public IActionResult GetConfigurations()
        {
            var result = new List<string>();

            foreach (var key in _configuration.AsEnumerable())
            {
                result.Add($"Key: {key.Key}, value: {key.Value}");
            }

            return Ok(result);
        }
    }
}
```

启动程序，访问：<https://localhost:5001/config>

不仅得到 appsettings.json 的配置， 还可以得到环境变量配置

可以在 ConfigureAppConfiguration 中清除所有配置，再添加自己需要的配置，后面添加的配置会覆盖前面的配置

```
.ConfigureAppConfiguration((hostingContext, config) =>
{
    config.Sources.Clear();

    var env = hostingContext.HostingEnvironment;

    config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
        .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
})
```

启动程序，访问：<https://localhost:5001/config>

这样可以得到自己添加的配置

#### 层级对象配置到 key-value 键值对转换 <a href="#ceng-ji-dui-xiang-pei-zhi-dao-keyvalue-jian-zhi-dui-zhuan-huan" id="ceng-ji-dui-xiang-pei-zhi-dao-keyvalue-jian-zhi-dui-zhuan-huan"></a>

appsettings.json

```
{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*"
}
```

通过冒号读取下一级配置（Windows），Linux 上通过下划线

```
[HttpGet]
public IActionResult GetConfigurations()
{
    var result = new List<string>();

    //foreach (var key in _configuration.AsEnumerable())
    //{
    //    result.Add($"Key: {key.Key}, value: {key.Value}");
    //}

    return Content(string.Format("Default Log Level: {0}", _configuration["Logging:LogLevel:Default"]));
}
```

启动程序，输出如下：

```
Default Log Level: Debug
```

#### 通过环境变量修改日志级别 <a href="#tong-guo-huan-jing-bian-liang-xiu-gai-ri-zhi-ji-bie" id="tong-guo-huan-jing-bian-liang-xiu-gai-ri-zhi-ji-bie"></a>

在 launcSettings.json 中添加 Trace 日志级别

```
"environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development",
        "Logging__LogLevel__Default": "Trace"
      }
```

在 CreateHostBuilder 的时候添加环境变量配置

```
config.AddEnvironmentVariables();
```

启动程序，输出如下：

```
Default Log Level: Trace
```

#### 通过命令行修改日志级别 <a href="#tong-guo-ming-ling-hang-xiu-gai-ri-zhi-ji-bie" id="tong-guo-ming-ling-hang-xiu-gai-ri-zhi-ji-bie"></a>

CreateHostBuilder

```
config.AddCommandLine(source =>
{
    source.Args = args;
});
```

在命令行中设置

```
set Logging__LogLevel__Default=Warning
```

### Options <a href="#options" id="options"></a>

* 通过 ConfigurationBinder 操作 Options
* 通过 Configure 绑定 Option

#### 通过 ConfigurationBinder 操作 Options <a href="#tong-guo-configurationbinder-cao-zuo-options" id="tong-guo-configurationbinder-cao-zuo-options"></a>

新建 MyOption.cs

```
namespace HelloApi
{
    public class MyOption
    {
        public string Name { get; set; }

        public int Age { get; set; }
    }
}
```

在 appsettings.json 中新增一个节点

```
"MyOption": {
    "Name": "Mingson",
    "Age": 25 
  },
```

在 ConfigureServices 中绑定

```
var myOption = new MyOption();
Configuration.GetSection("MyOption").Bind(myOption);
// 单例注入到全局中
services.AddSingleton(myOption);
```

在 ConfigController 中注入，与获取

```
private readonly MyOption _myOption;

public ConfigController(IConfiguration configuration, MyOption myOption)
{
    _configuration = configuration;
    _myOption = myOption;
}

[HttpGet("option")]
public IActionResult GetOption()
{
    return Ok(_myOption);
}
```

启动程序，访问：<https://localhost:5001/config/option>

输出如下：

```
{"name":"Mingson","age":25}
```

通过 Get 的方式

```
myOption = Configuration.GetSection("MyOption").Get<MyOption>();
```

#### 通过 Configure 绑定 Option <a href="#tong-guo-configure-bang-ding-option" id="tong-guo-configure-bang-ding-option"></a>

IOptions

* IOptions 被注册为 singletone，不支持为可命名的配置
* IOptionsSnapshot 被注册为 scoped，支持为可命名的配置
* IOptionsMonitor 被注册为 singletone，会被通知，支持重载配置，支持为可命名的配置

**IOptions**

```
// 直接注入到容器中
services.Configure<MyOption>(Configuration.GetSection("MyOption"));
```

通过 IOptions 注入

```
public ConfigController(IConfiguration configuration, IOptions<MyOption> myOption)
{
    _configuration = configuration;
    _myOption = myOption.Value;
}
```

启动程序可以得到同样的输出

**IOptionsSnapshot**

```
public ConfigController(IConfiguration configuration, IOptionsSnapshot<MyOption> myOption)
{
    _configuration = configuration;
    _myOption = myOption.Value;
}
```

启动程序，修改配置，刷新浏览器，可以获取到修改后的配置

**可命名的配置**

appsettings.json

```
"Jack": {
    "Name": "Jack",
    "Age": 16
  },
  "Peter": {
    "Name": "Peter",
    "Age": 18
  }
```

MyOption.cs

```
public const string PETER = "Peter";

public const string JACK = "Jack";
```

ConfigureServices

```
services.Configure<MyOption>("Peter", Configuration.GetSection("Peter"));
services.Configure<MyOption>("Jack", Configuration.GetSection("Jack"));
```

ConfigController

```
_myOption = myOption.Get(MyOption.PETER);
_myOption = myOption.Get(MyOption.JACK);
```

启动程序即可读取不同命名的配置

**IOptionsMonitor**

```
public ConfigController(IConfiguration configuration, IOptionsMonitor<MyOption> myOption)
{
    _configuration = configuration;
    _myOption = myOption.CurrentValue;

    // 配置变化处理
    myOption.OnChange(option =>
    {

    });
}
```

#### option 验证 <a href="#option-yan-zheng" id="option-yan-zheng"></a>

**属性验证标签**

```
namespace HelloApi
{
    public class MyConfigOptions
    {
        public const string MyConfig = "MyConfig";

        [RegularExpression(@"^[a-zA-Z''-'\s]{1,40}$")]
        public string Key1 { get; set; }

        [Range(0, 1000, ErrorMessage = "Value for {0} must be between {1} and {2}.")]
        public int Key2 { get; set; }

        public int Key3 { get; set; }
    }
}
```

**绑定后校验**

ConfigureServices

```
services.AddOptions<MyOption>().Bind(Configuration.GetSection("MyOption")).ValidateDataAnnotations();
```

MyOption

```
[Range(1, 20)]
public int Age { get; set; }
```

启动程序，输出如下：

```
OptionsValidationException: DataAnnotation validation failed for members: 'Age' with the error: 'The field Age must be between 1 and 20.'.
```

#### PostConfigure <a href="#postconfigure" id="postconfigure"></a>

当配置被读取出来的时候会被执行

```
services.PostConfigure<MyOption>(option =>
{
    if (option.Age == 20)
    {
        option.Age = 19;
    }
});
```

### GitHub源码链接： <a href="#github-yuan-ma-lian-jie" id="github-yuan-ma-lian-jie"></a>

<https://github.com/MingsonZheng/ArchitectTrainingCamp/tree/main/HelloApi>
