ASP.NET Core MVC における構成ファイル appsettings.json からの値取得

以前の以下の投稿で、Visual Studio 2017 において、標準でテンプレートに追加された ASP.NET Core MVC では、構成ファイル web.config から、JSON 形式の appsettings.json に変更されたことに触れました。 

- [Visual Studio 2017 による ASP.NET Core MVC 開発](http://qiita.com/hiromasa-masuda/items/5b9ff175aac3d1ef6ca6)

この appsettings.json ですが、もちろん、今までの web.config のように、カスタムでアプリケーション固有の構成情報を定義できます。

もちろん、以下の方法を使って、カスタムの構成情報を定義した JSON 形式ファイルを作成し、逆シリアライズにより構成情報をロードする方法もありますが、今回は、ASP.NET Core MVC で用意された機構を使った方法により値を取得します。

- [.NET Core 環境におけるオブジェクトとファイルの JSON シリアライズ、JSON 逆シリアライズ](http://qiita.com/hiromasa-masuda/items/d63bbc042f6b81ee08a5)
- [ASP.NET Core MVC における Server.MapPath によるコンテンツ ルート パスの取得](http://qiita.com/hiromasa-masuda/items/dddd29bb8937a12df12c)

構成ファイルのロードは、Startup.Starup メソッドで定義されています。

```csharp:Startup.cs

public class Startup
{
        public Startup(IHostingEnvironment env)
        {
            //構成ファイル、環境変数等から、構成情報をロード
            var builder = new ConfigurationBuilder()
                .SetBasePath(env.ContentRootPath)
                .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
                .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
                .AddEnvironmentVariables();

            //構成情報をプロパティに設定
            Configuration = builder.Build();
        }

        public IConfigurationRoot Configuration { get; }

}
```

この時、appsettings.json を以下のように定義したとします。

```json:appsettings.json

{
  "UserSettings": {
    "IsDemoMode": false,
    "DefaultUser": {
      "Name": "羽柴 秀長",
      "Age": 51
    }
  },
  "Logging": {
    "IncludeScopes": false,
    "LogLevel": {
      "Default": "Warning"
    }
  }
}
```
**Starup クラス内のスコープ**であれば、Configuration プロパティ経由で、値を取得できます。
IConfigurationRoot.GetValue<>() メソッド、もしくは、文字列の場合は、インデクサで、構成情報にアクセスできます。

```csharp:Startup.cs

public class Startup
{

        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
        {
            loggerFactory.AddConsole(Configuration.GetSection("Logging"));
            loggerFactory.AddDebug();

            //各構成情報の取得
            //ブール値のロード
            bool isDemoMode = this.Configuration.GetValue<bool>("UserSettings:IsDemoMode");
            //文字列値のロードは、インデクサで指定可能
            string defaultUserName = this.Configuration["UserSettings:DefaultUser:Name"];
            //int 値のロード
            int defaultUserAge = this.Configuration.GetValue<int>("UserSettings:DefaultUser:Age");


            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
                app.UseBrowserLink();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
            }

            app.UseStaticFiles();

            app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: "default",
                    template: "{controller=Home}/{action=Index}/{id?}");
            });
         }
}

```

Starup.Configuration プロパティ経由での取得方法は、MVC のコントローラー側で利用できませんので、別の方法を利用します。

まず、JSON に対応するクラスを定義します。すべての構造に対するクラスではなく、構成情報単位で定義すると良いと思います。
例) ユーザー設定、ページ設定、DB 接続設定等の単位にクラスを定義する
この理由は、最後に説明したいと思います。

```csharp:UserSettings.cs

    public class UserSettings
    {
        public bool IsDemoMode { get; set; }
        public DefaultUser DefaultUser { get; set; }
    }

    public class DefaultUser
    {
        public string Name { get; set; }
        public int Age { get; set; }
    }

```
このように、JSON に対応したクラスの定義を行う場合、JSON 文字列をコピーし、対象のクラス ファイル上で、[編集] - [形式を選択して貼り付け] - [JSON をクラスとして貼り付ける] を選択すると、クラスの定義をペースト出来るので便利です。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/172205/fe4c5daf-6448-da46-270a-67bfca5edca9.png)

Startup.ConfigureServices メソッドにおいて、構成情報を定義したクラスにバインドします。

```csharp:Startup.cs

public class Startup
{
    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        // Add framework services.
        services.AddMvc();

        //構成情報から、UserSettings クラスへバインド
        services.Configure<UserSettings>(this.Configuration.GetSection("UserSettings"));    
    }
}
```
最後に、MVC のコントローラー側での構成情報の取得の実装です。
コンストラクターを定義し、Startup.ConfigureServices においてバインドを行ったクラスをラップする IOptions インターフェースを引数とします。

```csharp:HomeController.cs

public class HomeController : Controller
{
    /// <summary>
    /// ユーザー設定情報を保持
    /// </summary>
    private readonly UserSettings _userSettings = null;

    /// <summary>
    /// コンストラクターを定義し、引数に構成情報を取得するクラスを定義する。
    /// </summary>
    /// <param name="userSettings"></param>
    public HomeController(IOptions<UserSettings> userSettings)
    {
        //ユーザー設定情報インスタンスをフィールドに保持
        this._userSettings = userSettings.Value;
    }

    public IActionResult Index()
    {
        return View(this._userSettings);
    }
}
```

ここで、すべての設定を持つクラスを定義し、引数で渡す方法もありますが、コントローラーで必要とする情報をコードで視覚化するためにも最小限の構成情報を渡すようにすることが良いと考えています。

もちろん、複数の構成情報クラスをバインドし、コントローラーのコンストラクターの引数で複数の構成情報クラスを取得することもできます。

単にコード量を減らすことが可読性をあげることではなく、意味読みできるコードこそ可読性が高いといえると考えています。

サンプル コードは、以下より参照ください。
https://github.com/Hiromasa-Masuda/dotnetcoreSample/tree/master/ConfigurationSample