1、前言

在大多数的应用程序中,一些参数需要写在配置文件里,以此增加系统的灵活性。在ASP.NET时代,配置参数一般会写在web.config文件中,其本质上是对XML文件的读取和写入。而在ASP.NET Core中,配置文件变成了appsettings.json文件。相较于XMLJSON文件更加轻量且灵活,下面就来介绍一下如何在ASP.NET Core中对其进行读写操作。

2、添加配置参数

打开appsettings.json文件,添加如下配置项。如果对JSON文件的格式不熟悉,建议先了解一下其格式规范,其代码如下所示:

{
    "Logging": {
        "LogLevel": {
            "Default": "Information",
            "Microsoft": "Warning",
            "Microsoft.Hosting.Lifetime": "Information"
        }
    },
    "AllowedHosts": "*",
    "ConnectionStrings": {
        "ConnectionString": "Data Source=DSF-PC;Initial Catalog=Dao;User ID=sa;Password=123456"
    },
    "AppName": "ASP.NET Core API",   // 应用程序名称
    "Author": "HerryDong",           // 作者
    "AppSettings": {
        "DefaultLanguage": "zh-CN",  // 默认语言
        "MinLevel": 10,              // 地图最小层级
        "MaxLevel": 16,              // 地图最大层级
        "Center": {
            "Longitude": 120,        // 地图中心点经度
            "Latitude": 30           // 地图中心点维度
        }
    }
}

3、基于配置节点名称的读取

3.1、注入IConfiguration接口

ASP.NET Core中,如果希望通过配置项的名称获取配置参数,那就需要用到IConfiguration接口。该接口已经默认存在于IoC容器中,无需手动注册。创建一个新的控制器HomeController,在构造函数中注入IConfiguration接口,代码如下所示:

using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;

namespace App.Controllers
{
    [Route("api/[controller]/[action]")]
    [ApiController]
    public class HomeController : ControllerBase
    {
        private readonly IConfiguration configuration;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="configuration"></param>
        public HomeController(IConfiguration configuration)
        {
            this.configuration = configuration;
        }
    }
}

3.2、读取配置参数

HomeController中添加一个Get方法,添加如下代码:

using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;

namespace App.Controllers
{
    [Route("api/[controller]/[action]")]
    [ApiController]
    public class HomeController : ControllerBase
    {
        private readonly IConfiguration configuration;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="configuration"></param>
        public HomeController(IConfiguration configuration)
        {
            this.configuration = configuration;
        }

        /// <summary>
        /// 读取配置参数
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        public string Get()
        {
            string connectionString = configuration.GetConnectionString("ConnectionString");
            string appName = configuration["AppName"];
            string author = configuration["Author"];
            string defaultLanguage = configuration["AppSettings:DefaultLanguage"];
            string minLevel = configuration["AppSettings:MinLevel"];
            string maxLevel = configuration["AppSettings:MaxLevel"];
            string longitude = configuration["AppSettings:Center:Longitude"];
            string latitude = configuration["AppSettings:Center:Latitude"];
            return connectionString + "\n" + appName + "\n" + author + "\n" +
                   defaultLanguage + "\n" + minLevel + "\n" + maxLevel + "\n" +
                   longitude + "\n" + latitude;
        }
    }
}

3.2.1、数据库连接字符串

对于数据库连接字符串ConnectionString,可以直接通过IConfiguration接口的GetConnectionString方法进行获取:

string connectionString = configuration.GetConnectionString("ConnectionString");

3.2.2、一级子节点

由于AppNameAuthor位于一级节点,因此可通过如下方式进行获取:

string appName = configuration["AppName"];
string author = configuration["Author"];

3.2.3、二级子节点

二级节点DefaultLanguageMinLevelMaxLevel位于一级节点AppSettings下,因此需要通过添加一个冒号:的方式进行获取:

string defaultLanguage = configuration["AppSettings:DefaultLanguage"];
string minLevel = configuration["AppSettings:MinLevel"];
string maxLevel = configuration["AppSettings:MaxLevel"];

3.2.4、三级子节点

三级节点LongitudeLatitude位于二级节点Center下,因此需要通过添加两个冒号::的方式进行获取:

string longitude = configuration["AppSettings:Center:Longitude"];
string Latitude = configuration["AppSettings:Center:Latitude"];

通过配置节点名称获取配置参数的方法比较直观,在写法上只需要注意配置节点所在的层级即可,程序运行结果如下图所示:

.net调用json数据类型 asp.net json数据读取_git

4、基于配置文件实体类的读取

4.1、构建配置文件实体类

ASP.NET Core也允许通过配置文件实体类的方式读取配置文件。首先根据appsettings.json中各个节点的层级关系构建相应实体类ConfigModel,其代码如下所示:

namespace App
{
    public class ConfigModel
    {
        public ConnectionStrings ConnectionStrings { get; set; }
        public string AppName { get; set; }
        public string Author { get; set; }
        public AppSettings AppSettings { get; set; }
    }

    public class ConnectionStrings
    {
        public string ConnectionString { get; set; }
    }

    public class AppSettings
    {
        public string DefaultLanguage { get; set; }
        public int MinLevel { get; set; }
        public int MaxLevel { get; set; }
        public Center Center { get; set; }
    }

    public class Center
    {
        public double Longitude { get; set; }
        public double Latitude { get; set; }
    }
}

4.2、注册配置文件实体类

在构建完配置文件实体类后,我们需要在Startup.cs文件中对其进行注册,调用services.Configure方法即可,代码如下所示:

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace App
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllers();

            // 注册配置文件实体类 
            services.Configure<ConfigModel>(Configuration);
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseHttpsRedirection();
            app.UseRouting();
            app.UseAuthorization();
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }
    }
}

4.3、注入IOptions接口

这里需要注意,原先通过配置节点名称读取配置参数值的时候,我们注入的是IConfiguration接口,而这里则需要换成IOptions接口,最后通过IOptions.Value获取实体类。由于IOptions接口默认存在于IoC容器中,因此无需手动对其进行注册。其代码如下所示:

using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Options;

namespace App.Controllers
{
    [Route("api/[controller]/[action]")]
    [ApiController]
    public class HomeController : ControllerBase
    {
        private readonly ConfigModel model;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="options"></param>
        public HomeController(IOptions<ConfigModel> options)
        {
            this.model = options.Value;
        }
    }
}

4.4、读取配置参数

现在我们已经获取了配置文件实体类,接下来的工作很简单了,由于是强类型操作,Visual Studio 2019会帮助你自动感知配置项,其代码如下所示:

using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Options;

namespace App.Controllers
{
    [Route("api/[controller]/[action]")]
    [ApiController]
    public class HomeController : ControllerBase
    {
        private readonly ConfigModel model;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="options"></param>
        public HomeController(IOptions<ConfigModel> options)
        {
            this.model = options.Value;
        }

        /// <summary>
        /// 读取配置参数
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        public string Get()
        {
            string connectionString = model.ConnectionStrings.ConnectionString;
            string appName = model.AppName;
            string author = model.Author;
            string defaultLanguage = model.AppSettings.DefaultLanguage;
            int minLevel = model.AppSettings.MinLevel;
            int maxLevel = model.AppSettings.MaxLevel;
            double longitude = model.AppSettings.Center.Longitude;
            double latitude = model.AppSettings.Center.Latitude;
            return connectionString + "\n" + appName + "\n" + author + "\n" +
                   defaultLanguage + "\n" + minLevel + "\n" + maxLevel + "\n" +
                   longitude + "\n" + latitude;
        }
    }
}

IConfiguration接口读取配置参数的方法相比,IOptions接口的优势是可以利用实体类进行映射和读取,这也可以杜绝由于写错配置项名称而引起错误的弊端。程序运行结果如下图所示:

.net调用json数据类型 asp.net json数据读取_git_02

5、写入配置文件

5.1、获取appsettings.json的路径

其实在实际开发过程中,微软并不推荐开发者对appsettings.json文件进行动态修改,一些需要动态配置的参数最好写在其他文件中,但在某些特殊情况下我们还是得这么干。话说回来,既然涉及到文件修改,那我们肯定得先获取这个文件的路径,否则一切免谈。在ASP.NET Core中,如果要获取一个文件的路径,我们可以使用IWebHostEnvironment接口,在使用时只需要将其注入控制器的构造函数即可,由于它已经默认存在于IoC容器中,因此我们无需进行手动注册,代码如下所示:

using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using System.IO;

namespace App.Controllers
{
    [Route("api/[controller]/[action]")]
    [ApiController]
    public class HomeController : ControllerBase
    {
        private readonly IWebHostEnvironment webHostEnvironment;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="webHostEnvironment"></param>
        public HomeController(IWebHostEnvironment webHostEnvironment)
        {
            this.webHostEnvironment = webHostEnvironment;
        }

        /// <summary>
        /// 获取文件路径
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        public string Get()
        {
            string filePath = Path.Combine(webHostEnvironment.ContentRootPath, "appsettings.json");
            return filePath;
        }
    }
}

运行一下程序,发现可以正确获取appsettings.json文件的路径,如下图所示:

.net调用json数据类型 asp.net json数据读取_.net调用json数据类型_03

5.2、修改配置参数

我们可以借助Newtonsoft.Json来实现配置参数的修改,使用NuGet将其引入,如下图所示:

.net调用json数据类型 asp.net json数据读取_ASP.NET Core_04

using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Options;
using Newtonsoft.Json.Linq;
using System.IO;

namespace App.Controllers
{
    [Route("api/[controller]/[action]")]
    [ApiController]
    public class HomeController : ControllerBase
    {
        private readonly IWebHostEnvironment webHostEnvironment;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="webHostEnvironment"></param>
        public HomeController(IWebHostEnvironment webHostEnvironment)
        {
            this.webHostEnvironment = webHostEnvironment;
        }

        /// <summary>
        /// 修改配置参数
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        public string Get()
        {
            try
            {
                // 读取appsettings.json文本内容
                string filePath = Path.Combine(webHostEnvironment.ContentRootPath, "appsettings.json");
                string text = System.IO.File.ReadAllText(filePath);

                // 修改配置项
                JObject obj = JObject.Parse(text);
                obj["AppName"] = "API";
                obj["Author"] = "Herry";
                obj["AppSettings"]["DefaultLanguage"] = "en-US";
                obj["AppSettings"]["MinLevel"] = 15;
                obj["AppSettings"]["MaxLevel"] = 20;
                obj["AppSettings"]["Center"]["Longitude"] = 130;
                obj["AppSettings"]["Center"]["Latitude"] = 40;

                // 重新写入appsettings.json
                string result = obj.ToString();
                System.IO.File.WriteAllText(filePath, result);
                return "success";
            }
            catch
            {
                return "failed";
            }
        }
    }
}

运行程序,发现appsettings.json的配置项修改成功。但也可以发现一个问题:原先的注释都没了,因此最好不要对该文件进行动态修改。程序运行结果如下图所示:

.net调用json数据类型 asp.net json数据读取_配置文件_05

5.3、注入IOptionsSnapshot接口

现在我们已经实现了appsettings.json文件的修改,但如果仍旧使用IOptions接口读取配置参数会怎么样呢?看下面一段代码:

using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using Newtonsoft.Json.Linq;
using System.IO;

namespace App.Controllers
{
    [Route("api/[controller]/[action]")]
    [ApiController]
    public class HomeController : ControllerBase
    {
        private readonly IWebHostEnvironment webHostEnvironment;
        private readonly ConfigModel model;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="webHostEnvironment"></param>
        /// <param name="options"></param>
        public HomeController(IWebHostEnvironment webHostEnvironment,
                              IOptions<ConfigModel> options)
        {
            this.webHostEnvironment = webHostEnvironment;
            this.model = options.Value;
        }

        /// <summary>
        /// 修改配置参数
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        public string Get()
        {
            try
            {
                // 读取appsettings.json文本内容
                string filePath = Path.Combine(webHostEnvironment.ContentRootPath, "appsettings.json");
                string text = System.IO.File.ReadAllText(filePath);

                // 修改
                JObject obj = JObject.Parse(text);
                obj["AppName"] = "API";
                obj["Author"] = "Herry";
                obj["AppSettings"]["DefaultLanguage"] = "en-US";
                obj["AppSettings"]["MinLevel"] = 15;
                obj["AppSettings"]["MaxLevel"] = 20;
                obj["AppSettings"]["Center"]["Longitude"] = 130;
                obj["AppSettings"]["Center"]["Latitude"] = 40;

                // 重新写入appsettings.json
                string result = obj.ToString();
                System.IO.File.WriteAllText(filePath, result);
                return "success";
            }
            catch
            {
                return "failed";
            }
        }

        /// <summary>
        /// 读取配置参数
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        public string Get_2()
        {
            string connectionString = model.ConnectionStrings.ConnectionString;
            string appName = model.AppName;
            string author = model.Author;
            string defaultLanguage = model.AppSettings.DefaultLanguage;
            int minLevel = model.AppSettings.MinLevel;
            int maxLevel = model.AppSettings.MaxLevel;
            double longitude = model.AppSettings.Center.Longitude;
            double latitude = model.AppSettings.Center.Latitude;
            return connectionString + "\n" + appName + "\n" + author + "\n" +
                   defaultLanguage + "\n" + minLevel + "\n" + maxLevel + "\n" +
                   longitude + "\n" + latitude;
        }
    }
}

在执行完Get方法后,我们再执行Get_2方法,发现好像不太对,怎么读出来的还是修改前的配置参数?

.net调用json数据类型 asp.net json数据读取_ASP.NET Core_06


其实,如果对appsettings.json文件做了修改,那么就不能使用IOptions接口去读取配置参数了,因为该接口实例被注册为全局单例生命周期,因此无法读到最新的配置参数。在这种情况下,我们需要使用另一个接口来实现配置参数的读取,那就是IOptionsSnapshot接口,由于该接口被注册为域生命周期,因此每次http访问都能读取到最新配置,其代码如下所示:

using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using Newtonsoft.Json.Linq;
using System.IO;

namespace App.Controllers
{
    [Route("api/[controller]/[action]")]
    [ApiController]
    public class HomeController : ControllerBase
    {
        private readonly IWebHostEnvironment webHostEnvironment;
        private readonly ConfigModel model;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="webHostEnvironment"></param>
        /// <param name="options"></param>
        public HomeController(IWebHostEnvironment webHostEnvironment,
                              IOptionsSnapshot<ConfigModel> options)
        {
            this.webHostEnvironment = webHostEnvironment;
            this.model = options.Value;
        }

        /// <summary>
        /// 修改配置参数
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        public string Get()
        {
            try
            {
                // 读取appsettings.json文本内容
                string filePath = Path.Combine(webHostEnvironment.ContentRootPath, "appsettings.json");
                string text = System.IO.File.ReadAllText(filePath);

                // 修改
                JObject obj = JObject.Parse(text);
                obj["AppName"] = "API";
                obj["Author"] = "Herry";
                obj["AppSettings"]["DefaultLanguage"] = "en-US";
                obj["AppSettings"]["MinLevel"] = 15;
                obj["AppSettings"]["MaxLevel"] = 20;
                obj["AppSettings"]["Center"]["Longitude"] = 130;
                obj["AppSettings"]["Center"]["Latitude"] = 40;

                // 重新写入appsettings.json
                string result = obj.ToString();
                System.IO.File.WriteAllText(filePath, result);
                return "success";
            }
            catch
            {
                return "failed";
            }
        }

        /// <summary>
        /// 读取配置参数
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        public string Get_2()
        {
            string connectionString = model.ConnectionStrings.ConnectionString;
            string appName = model.AppName;
            string author = model.Author;
            string defaultLanguage = model.AppSettings.DefaultLanguage;
            int minLevel = model.AppSettings.MinLevel;
            int maxLevel = model.AppSettings.MaxLevel;
            double longitude = model.AppSettings.Center.Longitude;
            double latitude = model.AppSettings.Center.Latitude;
            return connectionString + "\n" + appName + "\n" + author + "\n" +
                   defaultLanguage + "\n" + minLevel + "\n" + maxLevel + "\n" +
                   longitude + "\n" + latitude;
        }
    }
}

运行程序,发现可以读取到最新的配置参数,结果如下图所示:

.net调用json数据类型 asp.net json数据读取_git_07

6、结语

本文主要介绍了ASP.NET Core中配置文件的读写方法。在实际开发过程中,如果涉及到项目迁移,即:之前已经存在很多配置参数,我们可以使用IConfiguration接口读取配置参数,如果是新的项目,则可以考虑使用IOptions接口读取,因为强类型的读取方法可以避免因配置项名称写错而引发的一系列问题。