在.NET Core中从appsettings.json获取值

338
我不确定我在这里错过了什么,但我无法从我的.NET Core应用程序中获取appsettings.json中的值。我的appsettings.json如下:
{
    "AppSettings": {
        "Version": "One"
    }
}

创业公司:
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"));
    }
}

型号:
public class AppSettings
{
    public string Version{ get; set; }
}

控制器:

public class HomeController : Controller
{
    private readonly AppSettings _mySettings;

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

_mySettings 总是为空。这里有什么我漏掉的吗?


3
阅读文档了解如何使用配置。你在启动类中设置的配置有误,请检查。 - poke
1
这可以通过使用IConfiguration的依赖注入来简化。在这里有解释:http://www.coding-issues.com/2018/10/read-values-from-appsettings-json-.net-core.html - Ranadheer Reddy
10
这个问题有很多截然不同的答案,这凸显了一个问题。这让我想知道,是不是读取设置文件并反序列化为使用在线json→c#类生成器创建的对象更容易一些。对我来说,没有强类型的设置似乎有些原始。 - Damien
24个回答

470

程序和启动类

ASP.NET Core 6.x

ASP.NET Core 6.x 对于 Program 类带来了一些重大变化:

  • 如果选择使用顶层语句,就没有了 Program.Main() 的模板代码
  • 隐式 using 指令
  • 不再需要 Startup 类,因为所有内容都在 Program 文件中
  • 引入了 WebApplicationWebApplicationBuilder

有人说这些变化对于新手学习 ASP.NET Core 是好事。但是我个人感觉正好相反。我认为将 ProgramStartup 分开更有意义,至少对我来说是这样。

总之...

下面是 Program.cs 的示例代码:

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

你可以从WebApplication.CreateBuilder()builder.Build()之间的部分看出,它是旧的ConfigureServices(IServiceCollection services)所做的工作。而在app.Run()之前的部分则是Startup中旧的Configure()所做的工作。
最重要的是,IConfiguration被注入到管道中,因此你可以在控制器中使用它。
ASP.NET Core 3.x引入了一些变化,试图支持其他方法,如worker services,因此它用更通用的主机构建器替换了web host。
// 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>();
            }; 
    }
}
Startup类看起来与2.x版本非常相似。
ASP.NET Core 2.x
Startup构造函数中不需要新建IConfiguration。它的实现将由DI系统注入。
// 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

你需要告诉Startup加载appsettings文件。

// 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();
    }
    ...
}

获取值

有很多方法可以从应用程序设置中获取您配置的值:

  • 使用ConfigurationBuilder.GetValue<T>的简单方式
  • 使用Options Pattern

假设您的appsettings.json文件如下所示:

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

简单方法

您可以通过将整个配置注入到控制器/类的构造函数中(通过 IConfiguration),并使用指定的键获取所需的值:

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

选项模式

ConfigurationBuilder.GetValue<T> 在只需要从应用程序设置中获取一个或两个值时非常好用。但是,如果您想从应用程序设置中获取多个值,或者不想在多个地方硬编码这些键字符串,使用选项模式可能更容易。选项模式使用类来表示层次结构。

要使用选项模式:

  1. 定义类来表示结构
  2. 注册与这些类绑定的配置实例
  3. IOptions<T> 注入到您想要获取值的控制器/类的构造函数中

1. 定义配置类来表示结构

您可以定义具有属性的类,这些属性需要与您的应用程序设置中的键完全匹配。类的名称不必与应用程序设置中的部分名称相匹配:

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. 注册配置实例

然后,您需要在启动时的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<>的重载扩展方法中没有看到以IConfiguration为参数的版本,那么您可能需要安装Microsoft.Extensions.Options.ConfigurationExtensions NuGet包。

对于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);
    }
}

37
从全栈 .net 到这一步真是一个可怕的倒退。 - Aaron
5
好的,对于.NET Core 3,您需要使用Microsoft.Extensions.Options.ConfigurationExtensions包,它可以很好地工作。 - Tomas Bruckner
15
我们需要这么多解释才能访问简单的应用程序设置,这太荒谬了……以前在.NET Framework中只需一行代码即可完成。我知道依赖注入通常是一件好事,但在这种情况下,它更像是个麻烦而不是帮助。这一次,C#似乎只考虑了代码,而不是开发人员——有点像Java。 - Jon Story
21
未来参考:GetValue<T> 方法位于 Microsoft.Extensions.Configuration.Binder Nuget 包中。 - Jérôme MEVEL
1
@variable:检查您启动项目内的Properties文件夹下的launchSettings.json,并查看您的IIS Express配置文件中ASPNETCORE_ENVIRONMENT的设置。如果存在,它应该在模板文件appsettings.json之上运行appsettings.{ENV}.json - David Liang
显示剩余13条评论

79
只需创建一个名为AnyName.cs的文件,并粘贴以下代码。
using System;
using System.IO;
using Microsoft.Extensions.Configuration;

namespace Custom
{
    static class ConfigurationManager
    {
        public static IConfiguration AppSetting { get; }
        static ConfigurationManager()
        {
            AppSetting = new ConfigurationBuilder()
                    .SetBasePath(Directory.GetCurrentDirectory())
                    .AddJsonFile("YouAppSettingFile.json")
                    .Build();
        }
    }
}

必须将YouAppSettingFile.json文件名替换为您的文件名。

您的.json文件应该如下所示。

{
    "GrandParent_Key" : {
        "Parent_Key" : {
            "Child_Key" : "value1"
        }
    },
    "Parent_Key" : {
        "Child_Key" : "value2"
    },
    "Child_Key" : "value3"
}

现在你可以使用它了。 别忘了在你想要使用的类中添加引用
using Custom;

获取值的代码。

string value1 = ConfigurationManager.AppSetting["GrandParent_Key:Parent_Key:Child_Key"];
string value2 = ConfigurationManager.AppSetting["Parent_Key:Child_Key"];
string value3 = ConfigurationManager.AppSetting["Child_Key"];

11
不要在生产环境中使用此方法。这种方法曾导致我们的Web API出现内存泄漏问题。如果您正在使用NetCore,您可以在任何地方注入IConfiguration,只需查看上面的答案即可。 - André Mantas
我不建议在各处注入IConfiguration,因为这可能会导致一些安全问题;相反,你可以创建一些抽象层来获取所需的配置,并在需要时使用它们。这样,你的代码更加安全,而且你的代码不会依赖于IConfiguration或任何实现。 - HellBaby
我找不到 ConfigurationBuilder。我正在使用 .NET Core 3.1。 - H. Pauwelyn

60

在David Liang关于Core 2.0的回答中,需要补充的是 -

appsettings.json文件与ASPNETCORE_ENVIRONMENT变量相关联。

ASPNETCORE_ENVIRONMENT可以设置为任何值,但框架支持三个值:DevelopmentStagingProduction。如果未设置ASPNETCORE_ENVIRONMENT,则默认为Production

对于这三个值,这些appsettings.ASPNETCORE_ENVIRONMENT.json文件可直接使用 - appsettings.Staging.jsonappsettings.Development.jsonappsettings.Production.json

上述三个应用程序设置json文件可用于配置多个环境。

例如 - appsettings.Staging.json

{
    "Logging": {
        "IncludeScopes": false,
        "LogLevel": {
            "System": "Information",
            "Microsoft": "Information"
        }
    },
    "MyConfig": "My Config Value for staging."
}

使用Configuration["config_var"]来检索任何配置值。

public class Startup
{
    public Startup(IHostingEnvironment env, IConfiguration config)
    {
        Environment = env;
        Configuration = config;
        var myconfig = Configuration["MyConfig"];
    }

    public IConfiguration Configuration { get; }
    public IHostingEnvironment Environment { get; }
}

1
嵌套对象怎么办? - Arthur Attout
11
可以通过 Configuration["MyConfig:SomethingNested"] 获取嵌套对象。 - WeHaveCookies
1
正如在文件 https://github.com/aspnet/AspNetCore/blob/master/src/DefaultBuilder/src/WebHost.cs 的第167行所示,ASP.NET Core目前加载 appsettings.json + appsettings.{env.EnvironmentName}.json。因此,声称ASP.NET Core仅加载开发、暂存和生产appsettings.json文件的说法是不正确的。 - mvdgun
1
我是不是每次都要设置Windows变量ASPNETCORE_ENVIRONMENT?在.Net 4中,这些事情简单多了。这些JSON迷们把它搞砸了。 - Toolkit
如果我更改了JSON文件中的值,那么我需要重新启动应用程序才能获取最新的值吗? - variable
显示剩余2条评论

46
我猜最简单的方法是通过DI。一个到达控制器的例子。
// StartUp.cs
public void ConfigureServices(IServiceCollection services)
{
    ...
    // For getting appsettings from anywhere
    services.AddSingleton(Configuration);
}

public class ContactUsController : Controller
{
    readonly IConfiguration _configuration;

    public ContactUsController(
        IConfiguration configuration)
    {
        _configuration = configuration;

        // Sample:
        var apiKey = _configuration.GetValue<string>("SendGrid:CAAO");
        ...
    }
}

10
阅读其他答案,这应该是最好的。 - Sith2021
1
我之前漏掉了 services.AddSingleton(Configuration); ,现在它可以工作了。 - Arturio

22

假设您在appsettings.json中有以下值。

  "MyValues": {
    "Value1": "Xyz"
  }

方法一:不使用依赖注入

在 .cs 文件中:

static IConfiguration conf = (new ConfigurationBuilder().SetBasePath(Directory.GetCurrentDirectory()).AddJsonFile("appsettings.json").Build());
public static string myValue1= conf["MyValues:Value1"].ToString();

方法2:使用依赖注入(推荐)

在 Startup.cs 文件中:

public IConfiguration Configuration { get; }
public Startup(IConfiguration configuration)
{
     Configuration = configuration;
}
public void ConfigureServices(IServiceCollection services)
{
     ...
     services.AddServices(Configuration);
}

在您的控制器中:
public class TestController : ControllerBase
{
    private string myValue1 { get; set; }
    public TestController(IConfiguration configuration)
    {
         this.myValue1 = configuration.GetValue<string>("MyValues:Value1");
    }
}

18

在Startup类的构造函数中,您可以使用注入的IConfiguration对象访问appsettings.json和许多其他设置:

Startup.cs构造函数

    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;

        //here you go
        var myvalue = Configuration["Grandfather:Father:Child"];

    }

public IConfiguration Configuration { get; }

appsettings.json 的内容

  {
  "Grandfather": {
    "Father": {
      "Child": "myvalue"
    }
  }

2
正是 'Configuration["Grandfather:Father:Child"]' 语法帮助了我。 - Jacques Olivier
2
这是一个非常出色的答案,它的结构清晰明了,简洁明了。沟通很好。 - jolySoft
假设我更改了JSON文件中的值,那么我需要重新启动应用程序才能获取最新值吗? - variable
@变量修改应用程序设置的json文件将导致自动应用程序重启。 - Shadi Alnamrouti

12
    public static void GetSection()
    {
        Configuration = new ConfigurationBuilder()
            .SetBasePath(env.ContentRootPath)
            .AddJsonFile("appsettings.json")
            .Build();

        string BConfig = Configuration.GetSection("ConnectionStrings")["BConnection"];

    }

1
我不同意@CarlosABS的观点,这是最佳答案:其他所有答案 - 出于某种神秘的原因 - 都假设你正在使用DI并依赖他们的答案。 然而,对我来说,这才是答案的精髓 :) 我不需要更多的东西,所以其他答案似乎有些臃肿。 - csstudent1418
4
需要补充的是,为了使其正常工作,您需要三个该死的NuGet包:Microsoft.Extensions.ConfigurationMicrosoft.Extensions.Configuration.FileExtensionsMicrosoft.Extensions.Configuration.Json。我理解在.NET Core中的整个模块化设计,但有时您实际上需要为每一行代码添加另一个包... - csstudent1418

5

.NET Core 3.X

无需在Startup.cs中创建新模型或进行设置。

控制器 添加新包 - using Microsoft.Extensions.Configuration;

public class HomeController : Controller
{
    private readonly IConfiguration _mySettings;

    public HomeController (IConfiguration mySettings)
    {
         _mySettings= mySettings;
    }
 
    //ex: you can get value on below function 
    public IEnumerable<string> Get()
    {
        var result = _config.GetValue<string>("AppSettings:Version"); // "One"
        return new string[] { result.ToString() };
    }
}

1
_config是什么? - Clayton C
什么是 _config? - Wajid khan

5
针对 ASP.NET Core 3.1,您可以按照以下指南进行操作:https://learn.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-3.1。在创建新的 ASP.NET Core 3.1 项目时,您将在 Program.cs 中看到以下配置行:
Host.CreateDefaultBuilder(args)

这使得以下操作成为可能:

  1. ChainedConfigurationProvider: 添加一个已存在的IConfiguration作为源。在默认配置情况下,将主机配置添加为第一个源,并设置为应用程序配置的第一个源。
  2. 使用JSON配置提供程序的appsettings.json。
  3. 使用JSON配置提供程序的appsettings.Environment.json。例如,appsettings.Production.json和appsettings.Development.json。
  4. 当应用程序在开发环境中运行时,可以使用App secrets。
  5. 使用环境变量配置提供程序的环境变量。
  6. 使用命令行配置提供程序的命令行参数。

这意味着您可以注入IConfiguration并使用字符串键获取值,甚至是嵌套值。例如:IConfiguration["Parent:Child"];

示例:

appsettings.json

{
  "ApplicationInsights":
    {
        "Instrumentationkey":"putrealikeyhere"
    }
}

WeatherForecast.cs

[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
    private static readonly string[] Summaries = new[]
    {
        "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
    };

    private readonly ILogger<WeatherForecastController> _logger;
    private readonly IConfiguration _configuration;

    public WeatherForecastController(ILogger<WeatherForecastController> logger, IConfiguration configuration)
    {
        _logger = logger;
        _configuration = configuration;
    }

    [HttpGet]
    public IEnumerable<WeatherForecast> Get()
    {
        var key = _configuration["ApplicationInsights:InstrumentationKey"];

        var rng = new Random();
        return Enumerable.Range(1, 5).Select(index => new WeatherForecast
        {
            Date = DateTime.Now.AddDays(index),
            TemperatureC = rng.Next(-20, 55),
            Summary = Summaries[rng.Next(Summaries.Length)]
        })
        .ToArray();
    }
}

我在哪里可以了解更多关于 IConfiguration["Parent:Child"] 语法的知识? - xr280xr
@xr280xr 你可以在这里找到语法细节 https://learn.microsoft.com/zh-cn/aspnet/core/fundamentals/configuration/?view=aspnetcore-3.1#hierarchical-configuration-data 以及这里 https://learn.microsoft.com/zh-cn/aspnet/core/fundamentals/configuration/?view=aspnetcore-3.1#configuration-keys-and-values 同一页不同部分,但该页面总体包含了详细信息。 - Craig
值得一提的是,使用选项模式是获取配置值的首选方法。https://learn.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-3.1#bind-hierarchical-configuration-data-using-the-options-pattern - Craig

4

在我的情况下,只需在Configuration对象上使用Bind()方法。然后将该对象作为单例添加到DI中。

var instructionSettings = new InstructionSettings();
Configuration.Bind("InstructionSettings", instructionSettings);
services.AddSingleton(typeof(IInstructionSettings), (serviceProvider) => instructionSettings);

指令对象可以根据您的需要设置多种复杂功能。

{  
 "InstructionSettings": {
    "Header": "uat_TEST",
    "SVSCode": "FICA",
    "CallBackUrl": "https://UATEnviro.companyName.co.za/suite/webapi/receiveCallback",
    "Username": "s_integrat",
    "Password": "X@nkmail6",
    "Defaults": {
    "Language": "ENG",
    "ContactDetails":{
       "StreetNumber": "9",
       "StreetName": "Nano Drive",
       "City": "Johannesburg",
       "Suburb": "Sandton",
       "Province": "Gauteng",
       "PostCode": "2196",
       "Email": "ourDefaultEmail@companyName.co.za",
       "CellNumber": "0833 468 378",
       "HomeNumber": "0833 468 378",
      }
      "CountryOfBirth": "710"
    }
  }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接