Getting value from appsettings.json in .NET Core Ask Question

Getting value from appsettings.json in .NET Core Ask Question

I am not sure what am I missing here, but I am not able to get the values from my appsettings.json in my .NET Core application. I have my appsettings.json as:

{
    "AppSettings": {
        "Version": "One"
    }
}

Startup:

public class Startup
{
    private IConfigurationRoot _configuration;
    public Startup(IHostingEnvironment env)
    {
        _configuration = new ConfigurationBuilder()
    }
    public void ConfigureServices(IServiceCollection services)
    {
      //Here I setup to read appsettings
      services.Configure<AppSettings>(_configuration.GetSection("AppSettings"));
    }
}

Model:

public class AppSettings
{
    public string Version{ get; set; }
}

Controller:

public class HomeController : Controller
{
    private readonly AppSettings _mySettings;

    public HomeController(IOptions<AppSettings> settings)
    {
        //This is always null
        _mySettings = settings.Value;
    }
}

_mySettings is always null. Is there something that I am missing here?

ベストアンサー1

Program and Startup class

ASP.NET Core 6.x

ASP.NET Core 6.x brings another big changes in the Program class:

  • No Program.Main() boilerplate if you select to use Top level statements
  • Implicit using directives
  • No Startup class as everything is in Program file
  • Introduce WebApplication and WebApplicationBuilder

Some said these changes are good for new comers to learn ASP.NET Core. I kind of felt the other way around. I think the separation of Program and Startup made more sense, at least to me.

Anyway...

This is how Program.cs would look like:

// Program.cs
public class Program
{
    public static void Main(string[] args)
    {
        var builder = WebApplication.CreateBuilder(args);

        builder.Services.AddControllersWithViews();

        var app = builder.Build();

        if (!app.Environment.IsDevelopment())
        {
            app.UseExceptionHandler("/errors");
            app.UseHsts();
        }

        app.UseHttpsRedirection();
        app.UseStaticFiles();

        app.UseRouting();

        app.UseAuthorization();

        app.MapControllerRoute(
            name: "areaRoute",
            pattern: "{area:exists}/{controller=home}/{action=index}/{id?}");
            
        app.MapControllerRoute(
            name: "default",
            pattern: "{controller=home}/{action=index}/{id?}");

        app.Run();
    }
}

You can kind of tell the section between WebApplication.CreateBuilder() and builder.Build() is what the old ConfigureServices(IServiceCollection services) used to do. And the section before app.Run() is what the old Configure() from Startup used to do.

Most importantly, IConfiguration is injected to the pipeline so you can use it on your controllers.

ASP.NET Core 3.x to 5

ASP.NET Core 3.x brings some changes that try to support other approaches such as worker services, so it replaces web host with a more generic host builder:

// Program.cs
public class Program
{
    public static void Main(string[] args)
    {
        CreateHostBuilder(args).Build().Run();
    }

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseStartup<Startup>();
            }; 
    }
}

The Startup class looks pretty similar to the 2.x version though.

ASP.NET Core 2.x

You don't need to new IConfiguration in the Startup constructor. Its implementation will be injected by the DI system.

// Program.cs
public class Program
{
    public static void Main(string[] args)
    {
        BuildWebHost(args).Run();
    }

    public static IWebHost BuildWebHost(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .UseStartup<Startup>()
            .Build();            
}

// Startup.cs
public class Startup
{
    public IHostingEnvironment HostingEnvironment { get; private set; }
    public IConfiguration Configuration { get; private set; }

    public Startup(IConfiguration configuration, IHostingEnvironment env)
    {
        this.HostingEnvironment = env;
        this.Configuration = configuration;
    }
}

ASP.NET Core 1.x

You need to tell Startup to load the appsettings files.

// Program.cs
public class Program
{
    public static void Main(string[] args)
    {
        var host = new WebHostBuilder()
            .UseKestrel()
            .UseContentRoot(Directory.GetCurrentDirectory())
            .UseIISIntegration()
            .UseStartup<Startup>()
            .UseApplicationInsights()
            .Build();

        host.Run();
    }
}

//Startup.cs
public class Startup
{
    public IConfigurationRoot Configuration { get; private set; }

    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();

        this.Configuration = builder.Build();
    }
    ...
}

Getting Values

There are many ways you can get the value you configure from the app settings:

Let's say your appsettings.json looks like this:

{
    "ConnectionStrings": {
        ...
    },
    "AppIdentitySettings": {
        "User": {
            "RequireUniqueEmail": true
        },
        "Password": {
            "RequiredLength": 6,
            "RequireLowercase": true,
            "RequireUppercase": true,
            "RequireDigit": true,
            "RequireNonAlphanumeric": true
        },
        "Lockout": {
            "AllowedForNewUsers": true,
            "DefaultLockoutTimeSpanInMins": 30,
            "MaxFailedAccessAttempts": 5
        }
    },
    "Recaptcha": { 
        ...
    },
    ...
}

Simple Way

You can inject the whole configuration into the constructor of your controller/class (via IConfiguration) and get the value you want with a specified key:

public class AccountController : Controller
{
    private readonly IConfiguration _config;

    public AccountController(IConfiguration config)
    {
        _config = config;
    }

    [AllowAnonymous]
    public IActionResult ResetPassword(int userId, string code)
    {
        var vm = new ResetPasswordViewModel
        {
            PasswordRequiredLength = _config.GetValue<int>(
                "AppIdentitySettings:Password:RequiredLength"),
            RequireUppercase = _config.GetValue<bool>(
                "AppIdentitySettings:Password:RequireUppercase")
        };

        return View(vm);
    }
}

Options Pattern

The ConfigurationBuilder.GetValue<T> works great if you only need one or two values from the app settings. But if you want to get multiple values from the app settings, or you don't want to hard code those key strings in multiple places, it might be easier to use Options Pattern. The options pattern uses classes to represent the hierarchy/structure.

To use options pattern:

  1. Define classes to represent the structure
  2. Register the configuration instance which those classes bind against
  3. Inject IOptions<T> into the constructor of the controller/class you want to get values on

1. Define configuration classes to represent the structure

You can define classes with properties that need to exactly match the keys in your app settings. The name of the class does't have to match the name of the section in the app settings:

public class AppIdentitySettings
{
    public UserSettings User { get; set; }
    public PasswordSettings Password { get; set; }
    public LockoutSettings Lockout { get; set; }
}

public class UserSettings
{
    public bool RequireUniqueEmail { get; set; }
}

public class PasswordSettings
{
    public int RequiredLength { get; set; }
    public bool RequireLowercase { get; set; }
    public bool RequireUppercase { get; set; }
    public bool RequireDigit { get; set; }
    public bool RequireNonAlphanumeric { get; set; }
}

public class LockoutSettings
{
    public bool AllowedForNewUsers { get; set; }
    public int DefaultLockoutTimeSpanInMins { get; set; }
    public int MaxFailedAccessAttempts { get; set; }
}

2. Register the configuration instance

そして、ConfigureServices()起動時にこの構成インスタンスを登録する必要があります。

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
...

namespace DL.SO.UI.Web
{
    public class Startup
    {
        ...
        public void ConfigureServices(IServiceCollection services)
        {
            ...
            var identitySettingsSection = 
                _configuration.GetSection("AppIdentitySettings");
            services.Configure<AppIdentitySettings>(identitySettingsSection);
            ...
        }
        ...
    }
}

注:service.Configure<>パラメーターとして受け取るオーバーロード拡張メソッドが表示されない場合は、 NuGet パッケージIConfigurationをインストールする必要がある可能性があります。Microsoft.Extensions.Options.ConfigurationExtensions

ASP.NET Core 6.xの場合

冒頭で述べた ASP.NET Core 6.x の変更により、次のようにセクションをバインドして DI に追加する必要があります。

// Program.cs
public class Program
{
    public static void Main(string[] args)
    {
        var builder = WebApplication.CreateBuilder(args);

        builder.Services.AddControllersWithViews();

        builder.Services.Configure<AppIdentitySettings>(
            builder.Configuration.GetSection("AppIdentitySettings")
        );

        var app = builder.Build();

        ...

        app.Run();
    }
}

これについてさらに詳しく読むここ

3. IOptionsを挿入する

最後に、値を取得するコントローラー/クラスに、IOptions<AppIdentitySettings>コンストラクターを介して注入する必要があります。

public class AccountController : Controller
{
    private readonly AppIdentitySettings _appIdentitySettings;

    public AccountController(IOptions<AppIdentitySettings> appIdentitySettingsAccessor)
    {
        _appIdentitySettings = appIdentitySettingsAccessor.Value;
    }

    [AllowAnonymous]
    public IActionResult ResetPassword(int userId, string code)
    {
        var vm = new ResetPasswordViewModel
        {
            PasswordRequiredLength = _appIdentitySettings.Password.RequiredLength,
            RequireUppercase = _appIdentitySettings.Password.RequireUppercase
        };

        return View(vm);
    }
}

おすすめ記事