在 .NET 6 使用 JSON 檔儲存設定
0 |
最近的 Side-Project 計劃用 Minimal API 寫個簡單的 LINE Notify 發送服務,靠單一 EXE 在本機跑 localhost 網站搞定所有事,以貫徹極簡主義。
使用 LINE Notify API 需註冊取得 client_id 及 client_secret,與使用者建立連動時要記錄 access_token,我打算用網頁輸入 client_id 及 client_secret 儲存,取代手動編輯 appsetting.json,至於使用者 access_token,因筆數有限,不值得為此扯上資料庫,用 CSV 或 JSON 儲存是較簡便有效率的解法。最後,我決定借用 .NET Core 的 IConfiguration 機制加上寫入功能來儲存設定,主要有以下考量:
- 除了 JSON 檔案,IConfiguration 還有 XML/INI、環境變數、命令列參數... 等其他 IConfigurationProvider,具有很大的擴充彈性。
- JSON 設定檔支援多設定檔內容合併(例如:appsettings.json 與 appsettings.Development.json)、指定設定檔為選擇性或必備、檔案異動時自動更新... 等好用功能。
- 內建 GetValue<T>() 方法,能以強型別方式存取設定值。
以下是分享我的做法。
ASP.NET Core 專案已參照 Microsoft.Extensions.Configuration.* 相關程式庫可直接使用 IConfiguration,若是 Console 專案,需參照 Microsoft.Extensions.Configuration.Json 及 Microsoft.Extensions.Configuration.Binder。
我定義了一個 LineNotifySettings 型別,包含 ClientId、Secret 及 Dictionary<string, string> Tokens 屬性以保存 client_id、client_secret 及使用者對映的 access_token。設定檔的讀取及更新則交由自訂 Config 類別實作。Config 建構式會先檢查 line-notify-settings.json 是否存在,若沒有就建一個空範本,之後透過 ConfigurationBuilder.AddJsonFile().Build() 建構 出讀取 line-notify-settings.json 的 IConfiguration 介面,指定 reloadOnChange: true 確保 .json 變更時會自動更新。Config 有個屬性 Settings => config.Get<LineNotifySettings>(),每次重新從 IConfiguration 讀取最新的 LineNotifySettings 內容。Config 也提供 SetAuthIdentity()、SaveAccessToken() 等修改設定值的方法,但因 IConfiguration 不包含寫入 API ,故 Save() 要自行將 LineNotifySettings JSON 序列化覆寫 line-notify-settings.json。
Config.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
namespace LineNotifyMinApi
{
public class Config
{
LineNotifySettings Settings => config.Get<LineNotifySettings>();
string settingFilePath;
const string settingFileName = "line-notify-settings.json";
IConfiguration config;
public Config(string basePath = null!)
{
basePath = basePath ?? AppContext.BaseDirectory;
settingFilePath = Path.Combine(basePath, settingFileName);
if (!File.Exists(settingFilePath)) Save(new LineNotifySettings());
config = new ConfigurationBuilder()
.SetBasePath(basePath)
.AddJsonFile(settingFileName, optional: false, reloadOnChange: true)
.Build();
}
public string ClientId => Settings.ClientId;
public string Secret => Settings.Secret;
public bool IsSet => !string.IsNullOrEmpty(ClientId) && !string.IsNullOrEmpty(Secret);
public void SetAuthIdentity(string clientId, string secret)
{
lock (config)
{
var settings = Settings;
settings.ClientId = clientId;
settings.Secret = secret;
Save(settings);
}
}
public string[] ListUsers() => Settings.Tokens.Keys.ToArray();
public string GetAccessToken(string userName) =>
Settings.Tokens.ContainsKey(userName) ?
Settings.Tokens[userName] : null!;
public void SaveAccessToken(string userName, string token)
{
lock (config)
{
var settings = Settings;
if (settings.Tokens.ContainsKey(userName))
throw new ApplicationException($"Token [{userName}] already exists");
settings.Tokens.Add(userName, token);
Save(settings);
}
}
public void Save(LineNotifySettings settings)
{
File.WriteAllText(settingFilePath,
JsonSerializer.Serialize(settings, new JsonSerializerOptions
{
WriteIndented = true
}));
}
}
public class LineNotifySettings
{
public string ClientId { get; set; } = null!;
public string Secret { get; set; } = null!;
public Dictionary<string, string> Tokens { get; set; } = new Dictionary<string, string>();
}
}
在 Program.cs 隨便寫幾行程式展示它的功能。
using LineNotifyMinApi;
var config = new Config();
int userNo = 1;
while (true)
{
Console.Clear();
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine("Tokens: " + string.Join(", ", config.ListUsers()));
Console.ForegroundColor = ConsoleColor.White;
Console.Write(@"1.Set ClientId
2.Add AccessToken
3.Refresh
Option: ");
string opt = Console.ReadLine() ?? "?";
switch (opt) {
case "1":
Console.Write("Client Id: ");
var clientId = Console.ReadLine();
config.SetAuthIdentity(clientId!, "NA");
break;
case "2":
config.SaveAccessToken($"User{userNo++}", "");
Task.Delay(500).Wait(); // Waiting for reloadOnChange
break;
case "3":
break;
default:
Console.WriteLine($"Unknown command - {opt}") ;
break;
}
}
如以下動畫,SetAuthIdentity() 與 SaveAccessToken() 後會看到 line-notify-settings.json 隨之改變,若我們手動修改 line-notify-settings.json,之後 config.Get<LineNotifySettings>() 也會讀到修改後的內容。這個特性在 Windows Service 模式下挺好用,不必重啟服務就完成設定值修改,方便又省事。
如此,我們成功借用 IConfiguration 介面建立簡單的 JSON 設定值儲存來源,提供以強型別方式讀取及修改設定。初步試用感覺不錯,提供大家參考。
Example of how to use IConfiguration to create a read/write store for applcation's settings.
Comments
Be the first to post a comment