iT邦幫忙

2021 iThome 鐵人賽

DAY 9
0
Modern Web

網站一條龍 - 從架站到前端系列 第 9

[Day09] 從 appsettings.json 取得設定

昨天我們新增了一個 UserServiceWithFile 操作本地端檔案來管理使用者資料,而檔案的路徑是寫死在程式碼裡面的。這種寫死的設定有幾個缺點:

  1. 不容易變更設定
    如果要變更設定,就必須修改程式,修改程式之後又要重新佈署,不但麻煩還有增加錯誤的風險。
  2. 資安疑慮
    程式碼終究要進入版控,機密的資訊跟著程式碼進版控會有資安的疑慮。昨天的範例只有檔案路徑所以還勉強可以接受,以後用 MySQL 管理資料時,如果讓資料庫的連線資訊進到版控那問題就大了。

比較好的作法是把組態設定寫在設定檔(appsettings.json)裡,再讓我們的程式從這裡讀取設定。我們今天要來介紹如何在 .NET Web API 專案中,中讀取組態設定。

把設定加入 appsettings.json

appsettings.json 是 .NET 5 Web API 專案預設的設定檔,我們的程式在執行的時候,會從這裡取得組態設定。但仔細看一下右邊的方案總管,會發現 appsettings.json 其實可以展開,展開後會看到 appsettings.Development.json。這兩者的差別是,debug 模式下 appsettings.Development.json 會蓋掉 appsettings.json 的設定。

由於 appsettings.Development.json 不會進版控(因為正式環境不會用它),我們就能放心的把資料庫連線等機密資訊存在這邊,愉快的 debug。等到程式部屬到正式環境之後,再用加密連線的方式,把機密資訊寫到正式的 appsettings.json。如此一來,我們就能避免因為版控洩漏機密資訊。

現在,讓我們把測試用的設定加入這兩個檔案:

// appsettings.json
{
  "Logging": {
	// 這個區塊不變
  },
  "TestConfiguration": {
    "EnableLog": false,
    "FileName": "" // 這邊先挖空才進版控,以後部屬的時候再補上去
  }
}
// appsettings.Development.json 
{
  "Logging": {
	// 這個區塊不變
  },
  "TestConfiguration": {
    "EnableLog": false,
    "FileName": "D:/TestUsers.csv"
  }
}

懶人作法 – 注入整個設定檔

要取得設定檔,一個最簡單的做法是直接把 Startup.cs 的 public 屬性 「Configuration」,以 IConfiguration 的形式註冊到服務容器。下面這行程式碼把 IConfiguration 這個介面以 singleton 的生命週期註冊到服務容器讓別人依賴,而實作這個介面的是「Configuration」這個屬性。

public void ConfigureServices(IServiceCollection services)
{
    // 其他部分不變
    services.AddSingleton<IConfiguration>(Configuration); // 加入這一行 
    services.AddScoped<IUserCRUD, UserServiceWithFile>();
}

然後一樣注入給 Controller,再從注入的IConfiguration 介面取得設定值

public class UserServiceWithFile : IUserCRUD
{
    private readonly string _fileName;
    private readonly List<User> _users;
    private readonly IConfiguration _config;

    public UserServiceWithFile(IConfiguration config)
    {
        _config = config;
        _fileName = _config.GetSection("TestConfiguration:FileName").Value;
        _users = ReadUsersFromFile(_fileName);
    }

   // 其他部分不變
}

上面的程式碼使用 GetSection 這個方法,從 appsettings.json 取得設定值。用 GetSection("xxx").Value 取得設定有幾點要注意

  • 如果設定值是巢狀的 json 物件,可以用冒號(:)指定內層的 key 值
  • 如果指定的 key 值不存在,會回傳 null
  • 取得的值不管 json 裡是什麼資料型態,這裡一律變成 string

改完之後,我們就成功的從設定檔中取得資訊了!

注入 IOptions

上面的做法很簡單而且已經能滿足我們的需求,但是筆者更喜歡接下來要介紹的作法。這個做法可以直接取得強型別的設定物件。這個方法也很簡單,只需要以下三個步驟

1 宣告一個用來儲存設定的類別,這邊要注意類別的屬性名稱要與 json 物件的 key 值一致,但不分大小寫。

public class UserServiceOptions
{
    public bool EnableLog { get; set; }
    public string FileName { get; set; }
}

2 在 Startup.cs 抽取設定檔的區段,將它對應到剛剛宣告的類別,接著註冊到服務容器

public void ConfigureServices(IServiceCollection services)
{
    // 其他部分不變
    services.Configure<UserServiceOptions>( // 改成這一行
        Configuration.GetSection("TestConfiguration"));  
    services.AddScoped<IUserCRUD, UserServiceWithFile>();
}

上面那段程式碼代表從 appsettings.json 中抽取 “TestConfiguration” 區段,然後把這個 json 物件對應到 UserServiceOptions 這個類別,最後以 IOptions 的介面註冊到服務容器。

3 注入到 Controller 並使用。

public class UserServiceWithFile : IUserCRUD
{
        private readonly string _fileName;
        private readonly List<User> _users;
        private readonly IOptions<UserServiceOptions> _option;

        public UserServiceWithFile(IOptions<UserServiceOptions> option)
        {
            _option = option;
            _fileName = _option.Value.FileName;
            _users = ReadUsersFromFile(_fileName);
        }

	   // 其他部分不變
}

改完收工,我們的 API 程式就能從設定檔讀取設定。以後要變更設定,只要修改 appsettings.json 裡的值,然後重啟程式就可以了。

明天開始,我們將一步一步地介紹怎麼把目前完成的專案丟上雲端執行。


上一篇
[Day08] Dependency Injection Part2 - 依賴介面
下一篇
[Day10] Google Cloud Platform 簡介
系列文
網站一條龍 - 從架站到前端33

尚未有邦友留言

立即登入留言