65.9K
CodeProject 正在变化。 阅读更多。
Home

ASP.NET 5 MVC 6 vNext 中的应用程序配置

starIconstarIconstarIconstarIconstarIcon

5.00/5 (4投票s)

2016年2月28日

MIT

7分钟阅读

viewsIcon

20707

设置和使用 ASP.NET 5 vNext 中的新配置系统

引言

(我的 web.config 去哪儿了?)

在新的 ASP.NET 5 框架中设置应用程序配置时,您会注意到的第一件事是 web.config 文件不见了。  XML 已像恐龙一样消失,取而代之的是 JSON。  起初这可能有点令人困惑,但一旦您掌握了它,事情就会变得简单得多。不再有繁琐的发布与调试转换,新的配置文件也更容易阅读和理解(至少对我来说)。  此外……我们还可以获得强类型配置数据的优势,这些数据可以分离到模型中。在本文中,我将引导您了解开始使用 vNext 新配置系统所需的一切。

工作原理

新的配置文件恰当地命名为 appsettings.json,尽管您可以随意命名。  当您使用脚手架向导创建新的 ASP.NET 5 Web 应用程序项目时,此文件会自动放置在项目根目录中。  我们还可以创建该文件的不同版本,以便为每个环境(如调试和发布)覆盖值,但稍后将详细介绍。

显示 appsettings.json 的解决方案资源管理器

Solution Explorer showing appsettings.json

appsettings.json 文件的默认内容。

{
  "Data": {
    "DefaultConnection": {
      "ConnectionString": "Server=(localdb)\\mssqllocaldb;Database=blah-blah-blah..."
    }
  },
  "Logging": {
    "IncludeScopes": false,
    "LogLevel": {
      "Default": "Verbose",
      "System": "Information",
      "Microsoft": "Information"
    }
  }
}

如上所示,默认内容以 JavaScript 对象表示法 (JSON) 格式提供了一些基本的应用程序设置。  在这里,我们将找到连接字符串,就像在旧的 web.config 中一样,这里还有一些日志记录配置值。  注意键值对的层次结构。  让我们暂时脱离 appsettings.json 文件(稍后会回来),看看 Startup.cs

首先快速说明

您还需要确保通过 Nuget 的新包系统加载了正确的包,方法是将以下行放入 project.json
注意:这些已包含在脚手架生成的项目中。

// Make sure you have these assemblies added under dependencies in project.json
  "dependencies": {
    ///...
    ///...
    "Microsoft.Extensions.Configuration.FileProviderExtensions" : "1.0.0-rc1-final",
    "Microsoft.Extensions.Configuration.Json": "1.0.0-rc1-final",
    "Microsoft.Extensions.Configuration.UserSecrets": "1.0.0-rc1-final",
    ///...
    ///...
  }

Startup.cs 文件的默认内容。

// These two usings are needed for the configuration we have below.
using Microsoft.AspNet.Hosting;
using Microsoft.Extensions.Configuration;

namespace HelloConfigWorld
{
    public class Startup
    {
        public Startup(IHostingEnvironment env)
        {
            // Set up configuration sources.
            var builder = new ConfigurationBuilder()
                .AddJsonFile("appsettings.json")
                .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true);

            if (env.IsDevelopment())
            {
                builder.AddUserSecrets();
            }

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

        public IConfigurationRoot Configuration { get; set; }

        /// The rest of Startup.cs.
        ///...
        ///...
        ///...

    }
}

Startup.cs 中,我们有一个 ConfigurationBuilder。 最简单的形式,“Configuration”是一系列“Providers”。 在我们创建 ConfigurationBuilder() 之后,我们只需添加提供程序,即通过 .AddJsonFile("appsettings.json") 添加 JSON 文件提供程序,同样,我们分别通过 builder.AddUserSecrets()builder.AddEnvironmentVariables() 添加用户机密提供程序或环境变量提供程序。 为了说明我的观点,请看下面来自Microsoft 文档的简单示例。在这里,我们做了一些完全不同的事情,我们添加了一个 MemoryConfigurationProvider(),但概念是相同的。如果您怀念 XML,有一个内置的 XML 提供程序,您甚至可以编写自己的提供程序以应对特殊情况。

var builder = new ConfigurationBuilder();
builder.Add(new MemoryConfigurationProvider());
var config = builder.Build();
config.Set("somekey", "somevalue");

// do some other work

string setting2 = config["somekey"]; // returns "somevalue"

覆盖配置值

现在您可能会想,他之前说过我不必担心使用 web.config 时那些恼人的转换。  好吧,让我向您展示如何根据当前环境处理覆盖配置。  提供程序按顺序添加,每个新提供程序都会覆盖前一个提供程序设置的值。  通过按顺序添加它们,我们可以设置基本值,然后被开发或发布值覆盖,最后被用户机密(我将在下周的文章中介绍)覆盖,最后被环境变量覆盖。  顺序是可配置的,但这是一个基本的最佳实践。  回顾默认的 Startup.cs,注意这些行。

var builder = new ConfigurationBuilder()
    .AddJsonFile("appsettings.json")
    .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true);

现在假设我们在开发环境中,我们将首先读取 appsettings.json,然后读取 appsettings.development.json,覆盖所有匹配的值。  让我们向项目中添加一个开发文件。  此文件中的值将覆盖 appsettings.json 中先前存在的所有值。  要添加新文件,请右键单击您的项目,然后选择“添加 -> 新建项”。  接下来从菜单中选择“ASP.NET 配置文件”并将其命名为 appsettings.development.json

You can use the Add New Item wizard to add new configuration files.

您可以使用“添加新项”向导添加新的配置文件。  再次执行此操作以创建 appsettings.release.json,您应该会在解决方案资源管理器底部看到它。

Solution Explorer showing all appsettings.json

显示所有 appsettings.json 的解决方案资源管理器。  下面的代码有助于说明值首先在一个基本文件中定义,然后如果这些值存在于链中后续添加的文件中,值如何被替换。  不存在于后续加载的提供程序中的值将保持其原始状态。

// appsettins.json
{
  "myKey": "baseValue",
  "anotherKey": "unchangedValue"
}

// appsettins.development.json
{
  "myKey": "developmentValue"
}

// appsettins.release.json
{
  "myKey": "releaseValue"
} 

// Set up configuration sources.
var builder = new ConfigurationBuilder()
   .AddJsonFile("appsettings.json")
   .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true);

Configuration = builder.Build();

// In development.
Configuration.Get("myKey");         //<-- developmentValue (overidden)
Configuration.Get("anotherKey");    //<-- unchangedValue

// In release.
Configuration.Get("myKey");         //<-- releaseValue (overidden)
Configuration.Get("anotherKey");    //<-- unchangedValue

就是这么简单。  您还可以根据其他环境(如暂存)覆盖值。  您的文件不必命名为 appsettings.json,您也可以轻松地使用 foo.json 并用 bar.json 等覆盖。  比旧方式灵活得多,不是吗?

在应用程序中使用配置

好的,现在我们已经配置了应用程序值,我们如何获取它们以便在控制器中使用呢?  首先,值得一提的是强类型值。  以前,在 web.config 时代,我们存储的所有都是 string,然后在应用程序中使用时,我们将它们解析成任何我们需要的类型。  现在,在新系统中,我们将值以任何需要的类型存储在 JSON 中。  整数作为数字,布尔值作为布尔值,等等。  不再需要强制转换值来使用它们。  我们通过创建 POCO 类并在启动时用我们的配置值“加载”它们来实现这一点。  接下来,我们将为 SMTP 客户端创建一些配置选项并在应用程序中使用它们。  让我们通过 appsettings.json 向应用程序添加 SMTP 配置。  我们可以根据环境在其他文件中覆盖它们。  也许我们在生产环境中使用不同的服务器和不同的凭据,但我们已经知道如何做到这一点,所以我将保持简单。

{
  "Data": {
    "DefaultConnection": {
      "ConnectionString": "Server=(localdb)\\mssqllocaldb;Database=blah-blah-blah..."
    }
  },
  "Logging": {
    "IncludeScopes": false,
    "LogLevel": {
      "Default": "Verbose",
      "System": "Information",
      "Microsoft": "Information"
    }
  },
  // Defined later and overridden in appsettings.development.json or appsettings.release.json.
  "SmtpOptions": {
    "UserName": "Troy",
    "Password": "Locke",
    "FromName": "Troy Locke",
    "FromAddress": "Troy@slnzero.com",
    "DeliveryMethod": "Network",
    "Host": "smtp.slnzero.com",
    "Port":25,
    "DefaultCredentials": false,
    "EnableSsl": false
  }
}

请注意,我们有 string,一个指定 Port 的整数,以及 DefaultCredentialsEnableSsl 的布尔值。  太棒了!  接下来,我们必须创建一个类来保存这些值,以便在应用程序中使用。  我们将创建一个匹配的 POCO 类,名为 SmtpOptions,如下所示。

// Our SmtpOptions POCO class will hold our settings at runtime.
namespace HelloConfigWorld
{
    public class SmtpOptions
    {
        public string UserName { get; set; }

        public string Password { get; set; }

        public string FromName { get; set; }

        public string FromAddress { get; set; }

        public string DeliveryMethod { get; set; }

        public string Host { get; set; }

        public int Port { get; set; }

        public bool DefaultCredentials { get; set; }

        public bool EnableSsl { get; set; }
    }
}

同样,请注意我们的配置选项具有强类型值,这些值直接从 JSON 配置文件 appsettings.json 映射到我们的 SmtpOptions 类,方法是在 Startup.cs 中添加一行。  让我们看看如何做到这一点。  再次查看您的 Startup.cs,您会注意到,在我们之前关注的区域下方,有一个名为 ConfigureServices() 的方法。  在下面添加如图所示的行。

// 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.AddEntityFramework()
        .AddSqlServer()
        .AddDbContext<ApplicationDbContext>(options =>
            options.UseSqlServer(Configuration["Data:DefaultConnection:ConnectionString"]));

    ///...
    ///...
    ///...
    
    // Here we add our new SmtpOption values and map them to our POCO we created.
    services.Configure<SmtpOptions>(Configuration.GetSection("SmtpOptions"));
}

接下来,我们需要能够在应用程序中引用这些值。  vNext 非常注重 DI(依赖注入),我们将使用它通过 IOptions 接口将我们的 SmtpOptions 类“注入”到我们需要它的应用程序中。  在构造函数中,我们只需传递一个 IOptions<SmtpOptions>,并将其分配给一个类变量或属性。  然后,我们可以在类中使用它及其所有值。  下面是从默认的 MessageService.cs 中提取的代码片段,它实现了我们的 options 类。

public class AuthMessageSender : IEmailSender, ISmsSender
{
    private readonly IOptions<SmtpOptions> _smtpOptions;
    public AuthMessageSender(IOptions<SmtpOptions> smtpOptions)
    {
        _smtpOptions = smtpOptions;
    }

    public Task SendEmailAsync(string email, string subject, string message)
    {
        // Use SMTP here to send an email.
        var host = _smtpOptions.Value.Host;
        var port = _smtpOptions.Value.Port;
        var userName = _smtpOptions.Value.UserName;
        var password = _smtpOptions.Value.Password;
        var fromName = _smtpOptions.Value.FromName;
        var fromAddress = _smtpOptions.Value.FromAddress;
        var deliverMethod = _smtpOptions.Value.DeliveryMethod;
        var defaultCredentials = _smtpOptions.Value.DefaultCredentials;
        var enableSsl = _smtpOptions.Value.EnableSsl;

        var msg = new MimeMessage();
        msg.From.Add(new MailboxAddress(fromName, fromAddress));
        msg.To.Add(new MailboxAddress(email, email));
        msg.Subject = subject;

        ///...
        ///...
        ///...

     }
}

致谢

您可能在 Startup.cs 示例中注意到 AddUserSecrets()AddEnvironmentVariables()。  为了避免这篇文章过长,我将它们省略了。  简而言之,这些方法可以“安全地”将配置设置存储在应用程序代码之外。  密码等永远不会出现在代码中,因此也不会存储在版本控制中,也不会在共享代码库的开发者之间传递。  通过从生产环境中的环境变量中提取配置值,我们大大降低了它们被泄露的可能性。  我打算在未来的文章中详细介绍这个主题。  另外,我曾说过没有 web.config,这只说对了一部分,项目 /wwwroot 目录下确实存在一个 web.config 文件,用于配置某些服务器值。

摘要

如您所见,新的 vNext 比旧的 web.config 有了巨大的改进。  它更加通用,我们可以完全控制应用程序的配置。  与旧系统相比,它更容易阅读、配置和设置。  并且使用强类型值是整体上更好的做法。  我希望本文能帮助您更好地理解和实现新的 ASP.NET 5 vNext 框架中的配置。

© . All rights reserved.