Ordering sensitive configuration #582

This commit is contained in:
John Luo 2016-01-25 17:32:22 -08:00
parent 3ea44c6f75
commit ae47bb21a6
7 changed files with 156 additions and 53 deletions

View File

@ -2,10 +2,8 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
namespace Microsoft.AspNetCore.Hosting
@ -19,20 +17,6 @@ namespace Microsoft.AspNetCore.Hosting
/// Builds an <see cref="IWebHost"/> which hosts a web application.
/// </summary>
IWebHost Build();
/// <summary>
/// Gets the raw settings to be used by the web host. Values specified here will override
/// the configuration set by <see cref="UseConfiguration(IConfiguration)"/>.
/// </summary>
IDictionary<string, string> Settings { get; }
/// <summary>
/// Specify the <see cref="IConfiguration"/> to be used by the web host. If no configuration is
/// provided to the builder, the default configuration will be used.
/// </summary>
/// <param name="configuration">The <see cref="IConfiguration"/> to be used.</param>
/// <returns>The <see cref="IWebHostBuilder"/>.</returns>
IWebHostBuilder UseConfiguration(IConfiguration configuration);
/// <summary>
/// Specify the <see cref="IServerFactory"/> to be used by the web host.
@ -63,11 +47,18 @@ namespace Microsoft.AspNetCore.Hosting
IWebHostBuilder Configure(Action<IApplicationBuilder> configureApplication);
/// <summary>
/// Add or replace a setting in <see cref="Settings"/>.
/// Add or replace a setting in the configuration.
/// </summary>
/// <param name="key">The key of the setting to add or replace.</param>
/// <param name="value">The value of the setting to add or replace.</param>
/// <returns>The <see cref="IWebHostBuilder"/>.</returns>
IWebHostBuilder UseSetting(string key, string value);
/// <summary>
/// Get the setting value from the configuration.
/// </summary>
/// <param name="key">The key of the setting to look up.</param>
/// <returns>The value the setting currently contains.</returns>
string GetSetting(string key);
}
}

View File

@ -0,0 +1,34 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
using Microsoft.Extensions.Configuration;
namespace Microsoft.AspNetCore.Hosting.Internal
{
public static class IConfigurationExtensions
{
// Temporary until Configuration/issues/370 is implemented.
public static IEnumerable<KeyValuePair<string, string>> GetFlattenedSettings(this IConfiguration configuration)
{
var stack = new Stack<IConfiguration>();
stack.Push(configuration);
while (stack.Count > 0)
{
var config = stack.Pop();
var section = config as IConfigurationSection;
if (section != null)
{
yield return new KeyValuePair<string, string>(section.Path, section.Value);
}
foreach (var child in config.GetChildren())
{
stack.Push(child);
}
}
}
}
}

View File

@ -4,7 +4,7 @@
using System.Collections.Generic;
using Microsoft.Extensions.Configuration;
namespace Microsoft.AspNetCore.Hosting
namespace Microsoft.AspNetCore.Hosting.Internal
{
public class WebHostConfiguration
{

View File

@ -2,7 +2,6 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.Versioning;
using Microsoft.AspNetCore.Builder;
@ -28,7 +27,7 @@ namespace Microsoft.AspNetCore.Hosting
private readonly IHostingEnvironment _hostingEnvironment;
private readonly ILoggerFactory _loggerFactory;
private IConfiguration _config;
private IConfiguration _config = new ConfigurationBuilder().AddInMemoryCollection().Build();
private WebHostOptions _options;
private Action<IServiceCollection> _configureServices;
@ -40,26 +39,12 @@ namespace Microsoft.AspNetCore.Hosting
// Only one of these should be set
private IServerFactory _serverFactory;
private IDictionary<string, string> _settings = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
public WebHostBuilder()
{
_hostingEnvironment = new HostingEnvironment();
_loggerFactory = new LoggerFactory();
}
/// <summary>
/// Gets the raw settings to be used by the web host. Values specified here will override
/// the configuration set by <see cref="UseConfiguration(IConfiguration)"/>.
/// </summary>
public IDictionary<string, string> Settings
{
get
{
return _settings;
}
}
/// <summary>
/// Add or replace a setting in <see cref="Settings"/>.
/// </summary>
@ -68,20 +53,18 @@ namespace Microsoft.AspNetCore.Hosting
/// <returns>The <see cref="IWebHostBuilder"/>.</returns>
public IWebHostBuilder UseSetting(string key, string value)
{
_settings[key] = value;
_config[key] = value;
return this;
}
/// <summary>
/// Specify the <see cref="IConfiguration"/> to be used by the web host. If no configuration is
/// provided to the builder, the default configuration will be used.
/// Get the setting value from the configuration.
/// </summary>
/// <param name="configuration">The <see cref="IConfiguration"/> to be used.</param>
/// <returns>The <see cref="IWebHostBuilder"/>.</returns>
public IWebHostBuilder UseConfiguration(IConfiguration configuration)
/// <param name="key">The key of the setting to look up.</param>
/// <returns>The value the setting currently contains.</returns>
public string GetSetting(string key)
{
_config = configuration;
return this;
return _config[key];
}
/// <summary>
@ -187,15 +170,6 @@ namespace Microsoft.AspNetCore.Hosting
private IServiceCollection BuildHostingServices()
{
// Apply the configuration settings
var configuration = _config ?? WebHostConfiguration.GetDefault();
var mergedConfiguration = new ConfigurationBuilder()
.Add(new IncludedConfigurationProvider(configuration))
.AddInMemoryCollection(_settings)
.Build();
_config = mergedConfiguration;
_options = new WebHostOptions(_config);
var services = new ServiceCollection();

View File

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.AspNetCore.Hosting.Internal;
using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.Extensions.Configuration;
@ -21,6 +22,16 @@ namespace Microsoft.AspNetCore.Hosting
return builder.UseConfiguration(WebHostConfiguration.GetDefault(args));
}
public static IWebHostBuilder UseConfiguration(this IWebHostBuilder builder, IConfiguration configuration)
{
foreach (var setting in configuration.GetFlattenedSettings())
{
builder.UseSetting(setting.Key, setting.Value);
}
return builder;
}
public static IWebHostBuilder UseCaptureStartupErrors(this IWebHostBuilder hostBuilder, bool captureStartupError)
{
return hostBuilder.UseSetting(WebHostDefaults.CaptureStartupErrorsKey, captureStartupError ? "true" : "false");

View File

@ -22,7 +22,7 @@ namespace Microsoft.AspNetCore.TestHost
public TestServer(IWebHostBuilder builder)
{
if (!builder.Settings.ContainsKey(WebHostDefaults.CaptureStartupErrorsKey))
if (string.IsNullOrEmpty(builder.GetSetting(WebHostDefaults.CaptureStartupErrorsKey)))
{
builder.UseCaptureStartupErrors(false);
}

View File

@ -124,9 +124,10 @@ namespace Microsoft.AspNetCore.Hosting
}
[Fact]
public void CaptureStartupErrorsByDefault()
public void DefaultConfigurationCapturesStartupErrors()
{
var hostBuilder = new WebHostBuilder()
.UseDefaultConfiguration()
.UseServer(new TestServer())
.UseStartup<StartupBoom>();
@ -146,6 +147,98 @@ namespace Microsoft.AspNetCore.Hosting
Assert.Equal("A public method named 'ConfigureProduction' or 'Configure' could not be found in the 'Microsoft.AspNetCore.Hosting.Fakes.StartupBoom' type.", exception.Message);
}
[Fact]
public void CodeBasedSettingsCodeBasedOverride()
{
var hostBuilder = new WebHostBuilder()
.UseSetting(WebHostDefaults.ServerKey, "ServerA")
.UseSetting(WebHostDefaults.ServerKey, "ServerB")
.UseServer(new TestServer())
.UseStartup<StartupNoServices>();
var host = (WebHost)hostBuilder.Build();
Assert.Equal("ServerB", host.ServerFactoryLocation);
}
[Fact]
public void CodeBasedSettingsConfigBasedOverride()
{
var settings = new Dictionary<string, string>
{
{ WebHostDefaults.ServerKey, "ServerB" }
};
var config = new ConfigurationBuilder()
.AddInMemoryCollection(settings)
.Build();
var hostBuilder = new WebHostBuilder()
.UseSetting(WebHostDefaults.ServerKey, "ServerA")
.UseConfiguration(config)
.UseServer(new TestServer())
.UseStartup<StartupNoServices>();
var host = (WebHost)hostBuilder.Build();
Assert.Equal("ServerB", host.ServerFactoryLocation);
}
[Fact]
public void ConfigBasedSettingsCodeBasedOverride()
{
var settings = new Dictionary<string, string>
{
{ WebHostDefaults.ServerKey, "ServerA" }
};
var config = new ConfigurationBuilder()
.AddInMemoryCollection(settings)
.Build();
var hostBuilder = new WebHostBuilder()
.UseConfiguration(config)
.UseSetting(WebHostDefaults.ServerKey, "ServerB")
.UseServer(new TestServer())
.UseStartup<StartupNoServices>();
var host = (WebHost)hostBuilder.Build();
Assert.Equal("ServerB", host.ServerFactoryLocation);
}
[Fact]
public void ConfigBasedSettingsConfigBasedOverride()
{
var settings = new Dictionary<string, string>
{
{ WebHostDefaults.ServerKey, "ServerA" }
};
var config = new ConfigurationBuilder()
.AddInMemoryCollection(settings)
.Build();
var overrideSettings = new Dictionary<string, string>
{
{ WebHostDefaults.ServerKey, "ServerB" }
};
var overrideConfig = new ConfigurationBuilder()
.AddInMemoryCollection(overrideSettings)
.Build();
var hostBuilder = new WebHostBuilder()
.UseConfiguration(config)
.UseConfiguration(overrideConfig)
.UseServer(new TestServer())
.UseStartup<StartupNoServices>();
var host = (WebHost)hostBuilder.Build();
Assert.Equal("ServerB", host.ServerFactoryLocation);
}
[Fact]
public void UseEnvironmentIsNotOverriden()
{