経緯
ASP.NET Core には appsettings.json 等の設定から読み込んだデータを検証する機能があります。
ASP.NET Core のオプション パターン : オプションの検証
上記の機能を利用すると簡単に検証を実装出来て非常に便利なのですが、検証のタイミングが DI コンテナから取り出したタイミングになってしまい設定ミスに気付くのが遅れてしまうことが何度かありました。
設定ミスはなるべく早く気づくようにしたいので、こちらの Github Issue の実装例を参考に、起動時に設定を検証する実装を試しましたので、備忘録を兼ねて実装と使い方の例を残します。
(2022/11/17 追記)
.NET 6 から ValidateOnStart メソッドが使えるようになり、これを使うとアプリ起動時 (app.Run()
の呼び出し時、のようです) で検証が実行されるようになりました
.NET 6 以降で本記事の「起動時に設定を検証する」挙動を実現するなら ValidateOnStart() を使用するべきと感じています。
環境
ASP.NET Core 3.1
実装
設定を検証する StartupFilter を定義します。
public class StartupOptionsValidationFilter<T> : IStartupFilter
{
public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next)
{
return builder =>
{
var options = builder.ApplicationServices.GetService(typeof(IOptions<>).MakeGenericType(typeof(T)));
if (options != null)
_ = ((IOptions<object>)options).Value;
next(builder);
};
}
}
上記の StartupFilter を DI に登録する拡張メソッドを定義します。
public static class OptionsBuilderValidationExtensions
{
public static OptionsBuilder<TOptions> <TOptions>(this OptionsBuilder<TOptions> optionsBuilder)
where TOptions : class
{
optionsBuilder.Services.AddTransient<IStartupFilter, StartupOptionsValidation<TOptions>>();
return optionsBuilder;
}
}
使い方例
設定ファイルをバインドするクラスを用意します。
今回は DataAnnotations による検証と、カスタムの検証ルール IsValid()
を用意しました。
public class SettingA
{
[Required]
public string OptionA { get; set; }
public bool UseOptionB { get; set; }
public string OptionB { get; set; }
public bool IsValid()
{
// UseOptionB が true の場合、OptionB の設定が必須
return !(UseOptionB && OptionB == null);
}
}
Startup.cs の ConfigureServices
メソッドで設定ファイルを DI に以下のように追加します。
public void ConfigureServices(IServiceCollection services)
{
services.AddOptions<SettingA>()
.Configure(option => Configuration.Bind("settingA", option))
.ValidateDataAnnotations() // DataAnnotations による検証
.Validate(option => option.IsValid()) // カスタムの検証ルール "IsValid()" による検証
.ValidateEagerly(); // 起動時に設定を検証する StartupFilter を DI に追加
services.AddControllers();
}