ASP.NET Core 7 啟動時驗證設定檔(appsettings.json)內容

關於 ASP.NET Core 的設定檔 appsettings.json ,筆曾經寫過兩篇文章,隨著專案的增加,又陸陸續續發現一些問題,於是又寫了本篇來解決,若不太熟習的讀者,可以先閱讀之前的文章。

延伸閱讀:
ASP.NET Core 6 讀取設定檔(appsettings.json)範例
ASP.NET Core 使用強型別讀取設定檔 appsettings.json 教學

使用上面的任意一篇,已經能夠很好的讀取設定檔了,不過還是有個問題,就是不支援驗證。
在程式啟動時,透過依賴注入設定檔,直到真的用到時才會將資料拿出來。不過如果資料有問題,也就是這時候才會發現。 那能不能一開始啟動程式時就驗證,剛好工程師也在旁邊,有問題能夠直接發現。
後來筆者發現了一個好用的方法,故再上來紀錄一下,希望能夠幫助需要的人。

和強型別那篇一樣,先建立一個與設定值對應的類別
    
public class BasicOptions
{
    public const string SectionName = "Basic";

    /// <summary>
    /// 檔案上傳最大容量(MB)
    /// </summary>
    public int MaxFileSizeInMB { get; set; } = 10;
}
    

設定檔內容:
    
{
  "Basic": {
    "MaxFileSizeInMB": "A10"
  }
}
    

只是在 Program.cs 注入時使用下面的方式:
    
builder.Services
    .AddOptions<BasicOptions>()
    .Bind(builder.Configuration.GetSection(BasicOptions.SectionName))
    .ValidateDataAnnotations()
    .ValidateOnStart();
    
var app = builder.Build();
    

下面有個完整 API 範例,用來示範讀取設定檔內容:
    
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using WebApplicationApiConfigValidate.Options;

[ApiController]
[Route("/api/[controller]")]
public class BasicControllers : ControllerBase
{
    private readonly BasicOptions _basicOptions;

    public BasicControllers(IOptions<BasicOptions> options)
    {
        _basicOptions = options.Value;
    }

    [HttpGet("size")]
    public IActionResult GetMaxFileSizeInMB()
    {
        return Ok(_basicOptions.MaxFileSizeInMB);
    }
}
    

API 路徑為: /api/basic/size

這樣如果設定檔內容和型別不符合時就會拋出例外: (將 MaxFileSizeInMB 設為 A10 ,拋出型別錯誤)
    
Unhandled exception. System.InvalidOperationException: Failed to convert configuration value at 'Basic:MaxFileSizeInMB' to type 'System.Int32'.
 ---> System.ArgumentException: A10 is not a valid value for Int32. (Parameter 'value')
 ---> System.FormatException: The input string 'A10' was not in a correct format.

    

只有型別驗證嗎? 當然不是,我們還可以在設定檔中加入各種驗證方式,例如使用 Range 將內容限制在 0 ~ 10 中間
    
using System.ComponentModel.DataAnnotations;

public class BasicOptions
{
    public const string SectionName = "Basic";

    /// <summary>
    /// 檔案上傳最大容量(MB)
    /// </summary>
    [Range(0, 10)]
    public int MaxFileSizeInMB { get; set; } = 10;
}
    

當我們在設定檔中將 MaxFileSizeInMB 設為 11 時就會出現下面的例外:
    
Unhandled exception. Microsoft.Extensions.Options.OptionsValidationException: DataAnnotation validation failed for 'BasicOptions' members: 'MaxFileSizeInMB' with the error: 'The field MaxFileSizeInMB must be between 0 and 10.'.
    

但目前還有一個小問題,就是每多一個設定檔類別,就會需要在 Program.cs 增加五行程式碼,非常麻煩。
其實我們可以透過建立幫助類的方式,之後要使用時就能夠很方便的只使用一行了

幫助類 OptionsHelper.cs:
    
public static class OptionsHelper
{
    public static IServiceCollection AddOptionsAndBind<T>(
        this IServiceCollection services,
        IConfiguration configuration,
        string sectionName
    ) where T : class
    {
        services
            .AddOptions<T>()
            .Bind(configuration.GetSection(sectionName))
            .ValidateDataAnnotations()
            .ValidateOnStart();

        return services;
    }
}
    

新的 Program.cs :
    
// builder.Services
//     .AddOptions<BasicOptions>()
//     .Bind(builder.Configuration.GetSection(BasicOptions.SectionName))
//     .ValidateDataAnnotations()
//     .ValidateOnStart();
//
// builder.Services
//     .AddOptions<JwtOptions>()
//     .Bind(builder.Configuration.GetSection(JwtOptions.SectionName))
//     .ValidateDataAnnotations()
//     .ValidateOnStart();

builder.Services
    .AddOptionsAndBind<BasicOptions>(builder.Configuration, BasicOptions.SectionName)
    .AddOptionsAndBind<JwtOptions>(builder.Configuration, JwtOptions.SectionName);
    
var app = builder.Build();
    

延伸閱讀:
ASP.NET Core 6 讀取設定檔(appsettings.json)範例
ASP.NET Core 使用強型別讀取設定檔 appsettings.json 教學 ASP.NET Core 6 使用 yaml 檔案取代預設的 appsettings.json 設定檔

留言