ASP.NET Core - 配置系統之配置讀取

啊晚發表於2023-03-02

一個應用要執行起來,往往需要讀取很多的預設好的配置資訊,根據約定好的資訊或方式執行一定的行為。

配置的本質就是軟體執行的引數,在一個軟體實現中需要的引數非常多,如果我們以 Hard Code(硬編碼)的方式寫在應用程式碼中,這樣配置就會很亂,而且後續也不容易修改。亂而多,而且不容易修改,這就需要一個統一管理的地方,最常見的方式就是配置檔案,這個也是開發人員非常熟悉的方式。

透過配置檔案設定好軟體應用執行的各種引數之後,我們在開發過程中需要能夠讀取到配置檔案的內容,根據配置內容進行軟體邏輯的判斷,實現完善的軟體行為邏輯。這一篇就是介紹 .NET Core 框架下怎麼使用配置系統,這也是 .NET Core 下的基礎設施之一。

1. 配置讀取

配置讀取是配置系統最基本的操作,幾乎是每個開發人員都會進行的操作,一個開發人員可能不清楚配置系統是怎麼實現的,配置檔案是怎麼解析的,但一定都做過讀取配置資訊的操作。.NET Core 框架下對於配置系統的使用最終暴露出來的介面是 ·IConfiguration·,它是供配置資料的統一檢視,配置讀取就透過這個介面的實現來進行。

預設建立的 ASP .NET Core 框架模板專案中預設有一個 appsettings.json 配置檔案,這個也是 ASP .NET Core 中最常用的配置檔案,在配置檔案中新增多一個 Settings 節點,內容如下:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "Settings": {
    "key1": "value1",
    "key2": 1,
    "key3": true,
    "key4": {
      "subKey1": "value",
      "subKey2": 1
    },
    "items": [ "item1", "item2", "item3" ]
  }
}

我們要讀取配置檔案中的內容,例如讀取“AllowedHosts”對於的值,只需要將其注入到需要的服務類中即可使用,ASP.NET Core 模板專案中使用Web主機構建和管理應用,在使用主機預設配置的時候已經將 Iconfiguration 服務註冊到依賴注入容器之中。

[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
    private readonly IConfiguration _configuration;
    public WeatherForecastController(IConfiguration configuration)
    {
        _configuration = configuration;
    }

    [HttpGet]
    public Task Get()
    {
        var allowedHosts = _configuration["AllowedHosts"];
        Console.WriteLine(allowedHosts);
        // 配置鍵不區分大小寫
        var allowedHosts = _configuration["AllowedHosts"];
        Console.WriteLine(allowedHosts);
        return Task.CompletedTask;
    }
}

上面這種讀取方式是索引器方式,最簡單也是基本的方式,配置被載入到記憶體中是以鍵值對的方式存在的,我們可以透過配置鍵讀取配置值,鍵是字串,不區分大小寫,讀取出來的值都是字串。

配置檔案中配置值往往不止一層,就像上面 appsettings.json 檔案中,Logging 節點下還有子節點,如果需要這種分層資料,可以使用 : 字元(英文冒號)分隔層次結構,例如獲取上面配置鍵 Default 對於的配置值。

// 以 : 作為分隔符,表示層級結構
var defalutLogLevel = _configuration["Logging:LogLevel:Default"];

如果配置值是陣列,需要讀取陣列中具體的某一個值,可以用該值在陣列中的索引作為key,例如讀取上面配置檔案中的items陣列中的 item2。

// 讀取陣列,可以用值在陣列中的索引作為key
var item2 = _configuration["Settings:items:1"];

這種方式讀取配置有挺多不方便的地方,例如配置值是數值型的時候,需要我們直接轉換,例如一次只能讀取到一個配置值。微軟透過 Microsoft.Extensions.Configuration.Binder 中的ConfigurationBinder 類提供了一些 IConfiguration 的靜態方法,用於獲取配置值時進行自動轉換和繫結。

(1) GetValue

透過ConfigurationBinder中的GetValue擴充套件方法,一樣可以透過配置鍵從配置系統中讀取對於的配置值。該方法有多個過載,支援透過泛型的方式進行資料型別轉換,並且支援設定預設值。

var defaultLogLevel2 = _configuration.GetValue<string>("Logging:LogLevel:Default");
// 配置資訊中不包含 "Logging:LogLevel:Default" 這個Key時,以預設值 "Error" 返回
var defaultLogLevel3 = _configuration.GetValue<string>("Logging:LogLevel:Default", "Error");

(2) GetSection

這樣子有些情況下仍然無法滿足我們的需要,某一些情況下我們會需要直接讀取配置中的一部分節點,例如直接讀取上面配置中的 LogLevel 部分。IConfiguration 中的 GetSection 方法可以透過 Key 直接讀取某一個子節點。該方法的返回值是 IConfigurationSection 型別,永遠不會返回 null,IConfigurationSection 實際上是一個 IConfiguration 的派生介面,也就是說我們還可以從 IConfigurationSection 再去獲取我們需要的具體的配置值。

var section = _configuration.GetSection("Settings:key4");
var defaultLogLevel4 = section["Default"];

(3) Get

上面講到透過 GetSection 獲取到了配置檔案中的一部分子節點,但是那樣仍然不方便,還是需要一個一個去讀取具體的值。可以透過 ConfigurationBinder.Get 擴充套件方法,將配置以強型別的方式繫結到物件上。

首先需要定義一個類來接收配置檔案中的節點資訊

public class KeyOptions
{
    public string subKey1 { get; set
    public int subKey2 { get; set; }
}

然後透過以下方式進行繫結:

var keyOption1 = _configuration.GetSection("Settings:key4").Get<KeyOptions>();

(4) Bind

ConfigurationBinder.Bind 擴充套件方法與 Get 方法類似,也是用於將配置繫結為強型別物件,不過Bind的方法是繫結到一個已例項化的物件上,需要提供一個已存在的物件。

var keyOption2 = new KeyOptions();
_configuration.GetSection("Settings:key4").Bind(keyOption2);

(5) Exists

上面說過,GetSection 方法獲取配置中的子節點,返回值永遠不會為 null。如果我們傳入了一個不存在的key,肯定是獲取不到對於的值的,這種情況下還是需要判斷對於的子節點到底是不是真正存在的,這時候可以使用 Exists 方法。

var section = _configuration.GetSection("settings");
var exist = section.Exists();

除此之外,還有一個 GetChildren 方法,無需引數,用於獲取到當前配置節點的所有直接子節點的集合。

以上就是 .NET Core 體系下配置系統讀取配置的基本介紹,涉及到的型別最主要的是 IConfiguration 介面,除此之外還有上面提到的 IConfigurationSection 介面,以及 IConfigurationRoot 介面。

IConfigurationRoot 表示配置的根節點,是IConfiguration的派生介面,以下為介面的定義:

public interface IConfigurationRoot: IConfiguration{
   // 存放了當前應用程式的所有配置提供程式
   IEnumerable<IConfigurationProvider> Providers { get; }
    // 強制從配置提供程式中過載配置
    void Reload();
}

這裡可以看到一個關鍵的屬性 IEnumerable Providers,這個就是配置系統中的配置資訊的來源,後面會仔細講這個。而 Reload 方法中最關鍵的也是呼叫集合中各個 IConfigurationProvider 進行資料載入。

IConfigurationSection 表示配置中的子節點,也是 IConfiguration 的派生介面,以下為介面的定義:

public interface IConfigurationSection: IConfiguration{
    // 該子節點在其父節點中所表示的 key,即直接對應的key
    string Key { get; }
    // 該子節點在配置中的全路徑(從根節點開始,到當前節點以:符號分隔的路徑)
    string Path { get; }
    // 該子節點的 value。如果該子節點是葉子節點,則Value為該節點對於的值,若其下存在子節點,則其始終為 null
    string Value { get; set; }
}

IConfigurationSection 介面透過以上三個屬性,結合 IConfiguration中的 GetChildren 方法來完整地表示的一個子節點,而 Exists 方法判斷節點是否為空,就是針對 IConfigurationSection 中的 Value 屬性和 GetChildren 方法來進行的。

public static class ConfigurationExtensions{
    public static bool Exists(thisIConfigurationSection section){
        if(section == null)
        {
            returnfalse;
        }
        returnsection.Value != null || section.GetChildren().Any();
    }
}


參考文章:

ASP.NET Core 中的配置 | Microsoft Learn
配置 - .NET | Microsoft Learn
理解ASP.NET Core - 配置(Configuration)



ASP.NET Core 系列:
目錄:ASP.NET Core 系列總結
上一篇:ASP.NET Core - 依賴注入(四)

相關文章