More hosting API changes

- Added IWebApplicationBuilder and moved it to Hosting.Abstractions as a
target for others to extend the web application builder.
- Made methods extension methods where possible
- Added UseUrls
This commit is contained in:
David Fowler 2016-01-06 18:08:57 -08:00
parent 46e3e25ec7
commit e72924796e
19 changed files with 373 additions and 242 deletions

View File

@ -35,8 +35,9 @@ namespace SampleStartups
.UseStartup<StartupBlockingOnStart>() .UseStartup<StartupBlockingOnStart>()
.Build(); .Build();
using (application.Start()) using (application)
{ {
application.Start();
Console.ReadLine(); Console.ReadLine();
} }
} }

View File

@ -32,12 +32,9 @@ namespace SampleStartups
var application = new WebApplicationBuilder() var application = new WebApplicationBuilder()
.UseConfiguration(config) .UseConfiguration(config)
.UseStartup<StartupConfigureAddresses>() .UseStartup<StartupConfigureAddresses>()
.UseUrls("http://localhost:5000", "http://localhost:5001")
.Build(); .Build();
var addresses = application.GetAddresses();
addresses.Add("http://localhost:5000");
addresses.Add("http://localhost:5001");
application.Run(); application.Run();
} }
} }

View File

@ -1,4 +1,5 @@
using System; using System;
using System.Collections.Generic;
using Microsoft.AspNet.Builder; using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Hosting; using Microsoft.AspNet.Hosting;
using Microsoft.AspNet.Http; using Microsoft.AspNet.Http;
@ -10,8 +11,8 @@ namespace SampleStartups
{ {
public class StartupExternallyControlled public class StartupExternallyControlled
{ {
private readonly IWebApplication _host; private IWebApplication _application;
private IDisposable _application; private readonly List<string> _urls = new List<string>();
// This method gets called by the runtime. Use this method to add services to the container. // This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit http://go.microsoft.com/fwlink/?LinkID=398940 // For more information on how to configure your application, visit http://go.microsoft.com/fwlink/?LinkID=398940
@ -30,15 +31,13 @@ namespace SampleStartups
public StartupExternallyControlled() public StartupExternallyControlled()
{ {
_host = new WebApplicationBuilder().UseStartup<StartupExternallyControlled>().Build();
// Clear all configured addresses
_host.GetAddresses().Clear();
} }
public void Start() public void Start()
{ {
_application = _host.Start(); _application = new WebApplicationBuilder()
.UseStartup<StartupExternallyControlled>()
.Start(_urls.ToArray());
} }
public void Stop() public void Stop()
@ -48,9 +47,7 @@ namespace SampleStartups
public void AddUrl(string url) public void AddUrl(string url)
{ {
var addresses = _host.GetAddresses(); _urls.Add(url);
addresses.Add(url);
} }
} }
} }

View File

@ -14,14 +14,11 @@ namespace SampleStartups
public static void Main(string[] args) public static void Main(string[] args)
{ {
var application = new WebApplicationBuilder() var application = new WebApplicationBuilder()
.UseServerFactory("Microsoft.AspNet.Server.Kestrel") // Set the server manually .UseServer("Microsoft.AspNet.Server.Kestrel") // Set the server manually
.UseApplicationBasePath(Directory.GetCurrentDirectory()) // Override the application base with the current directory .UseApplicationBasePath(Directory.GetCurrentDirectory()) // Override the application base with the current directory
.UseUrls("http://*:1000", "https://*:902")
.UseEnvironment("Development") .UseEnvironment("Development")
.UseWebRoot("public") .UseWebRoot("public")
.ConfigureLogging(loggerFactory =>
{
loggerFactory.AddProvider(new MyHostLoggerProvider());
})
.ConfigureServices(services => .ConfigureServices(services =>
{ {
// Configure services that the application can see // Configure services that the application can see

View File

@ -9,7 +9,7 @@ namespace Microsoft.AspNet.Hosting
/// <summary> /// <summary>
/// Represents a configured web application /// Represents a configured web application
/// </summary> /// </summary>
public interface IWebApplication public interface IWebApplication : IDisposable
{ {
/// <summary> /// <summary>
/// The <see cref="IFeatureCollection"/> exposed by the configured server. /// The <see cref="IFeatureCollection"/> exposed by the configured server.
@ -25,6 +25,6 @@ namespace Microsoft.AspNet.Hosting
/// Starts listening on the configured addresses. /// Starts listening on the configured addresses.
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
IDisposable Start(); void Start();
} }
} }

View File

@ -0,0 +1,28 @@
// 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;
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Hosting.Server;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
namespace Microsoft.AspNet.Hosting
{
public interface IWebApplicationBuilder
{
IWebApplication Build();
IWebApplicationBuilder UseConfiguration(IConfiguration configuration);
IWebApplicationBuilder UseServer(IServerFactory factory);
IWebApplicationBuilder UseStartup(Type startupType);
IWebApplicationBuilder ConfigureServices(Action<IServiceCollection> configureServices);
IWebApplicationBuilder Configure(Action<IApplicationBuilder> configureApplication);
IWebApplicationBuilder UseSetting(string key, string value);
}
}

View File

@ -12,7 +12,9 @@
"dependencies": { "dependencies": {
"Microsoft.AspNet.Http.Abstractions": "1.0.0-*", "Microsoft.AspNet.Http.Abstractions": "1.0.0-*",
"Microsoft.AspNet.FileProviders.Abstractions": "1.0.0-*", "Microsoft.AspNet.FileProviders.Abstractions": "1.0.0-*",
"Microsoft.Extensions.Configuration.Abstractions": "1.0.0-*" "Microsoft.AspNet.Hosting.Server.Abstractions": "1.0.0-*",
"Microsoft.Extensions.Configuration.Abstractions": "1.0.0-*",
"Microsoft.Extensions.DependencyInjection.Abstractions": "1.0.0-*"
}, },
"frameworks": { "frameworks": {
"net451": {}, "net451": {},

View File

@ -13,7 +13,6 @@ namespace Microsoft.AspNet.Hosting.WindowsServices
public class WebApplicationService : ServiceBase public class WebApplicationService : ServiceBase
{ {
private IWebApplication _application; private IWebApplication _application;
private IDisposable _applicationShutdown;
private bool _stopRequestedByWindows; private bool _stopRequestedByWindows;
/// <summary> /// <summary>
@ -41,7 +40,7 @@ namespace Microsoft.AspNet.Hosting.WindowsServices
} }
}); });
_applicationShutdown = _application.Start(); _application.Start();
OnStarted(); OnStarted();
} }
@ -50,7 +49,7 @@ namespace Microsoft.AspNet.Hosting.WindowsServices
{ {
_stopRequestedByWindows = true; _stopRequestedByWindows = true;
OnStopping(); OnStopping();
_applicationShutdown?.Dispose(); _application?.Dispose();
OnStopped(); OnStopped();
} }

View File

@ -0,0 +1,39 @@
// 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;
using Microsoft.Extensions.Configuration;
namespace Microsoft.AspNet.Hosting.Internal
{
// TODO: Remove this once https://github.com/aspnet/Configuration/pull/349 gets merged
internal class IncludedConfigurationProvider : ConfigurationProvider
{
public IncludedConfigurationProvider(IConfiguration source)
{
if (source == null)
{
throw new ArgumentNullException(nameof(source));
}
int pathStart = 0;
var section = source as IConfigurationSection;
if (section != null)
{
pathStart = section.Path.Length + 1;
}
foreach (var child in source.GetChildren())
{
AddSection(child, pathStart);
}
}
private void AddSection(IConfigurationSection section, int pathStart)
{
Data.Add(section.Path.Substring(pathStart), section.Value);
foreach (var child in section.GetChildren())
{
AddSection(child, pathStart);
}
}
}
}

View File

@ -32,6 +32,7 @@ namespace Microsoft.AspNet.Hosting.Internal
private IServiceProvider _applicationServices; private IServiceProvider _applicationServices;
private RequestDelegate _application; private RequestDelegate _application;
private ILogger<WebApplication> _logger;
// Only one of these should be set // Only one of these should be set
internal string StartupAssemblyName { get; set; } internal string StartupAssemblyName { get; set; }
@ -41,7 +42,7 @@ namespace Microsoft.AspNet.Hosting.Internal
// Only one of these should be set // Only one of these should be set
internal IServerFactory ServerFactory { get; set; } internal IServerFactory ServerFactory { get; set; }
internal string ServerFactoryLocation { get; set; } internal string ServerFactoryLocation { get; set; }
internal IServer Server { get; set; } private IServer Server { get; set; }
public WebApplication( public WebApplication(
IServiceCollection appServices, IServiceCollection appServices,
@ -97,29 +98,20 @@ namespace Microsoft.AspNet.Hosting.Internal
} }
} }
public virtual IDisposable Start() public virtual void Start()
{ {
Initialize(); Initialize();
var logger = _applicationServices.GetRequiredService<ILogger<WebApplication>>(); _logger = _applicationServices.GetRequiredService<ILogger<WebApplication>>();
var diagnosticSource = _applicationServices.GetRequiredService<DiagnosticSource>(); var diagnosticSource = _applicationServices.GetRequiredService<DiagnosticSource>();
var httpContextFactory = _applicationServices.GetRequiredService<IHttpContextFactory>(); var httpContextFactory = _applicationServices.GetRequiredService<IHttpContextFactory>();
logger.Starting(); _logger.Starting();
Server.Start(new HostingApplication(_application, logger, diagnosticSource, httpContextFactory)); Server.Start(new HostingApplication(_application, _logger, diagnosticSource, httpContextFactory));
_applicationLifetime.NotifyStarted(); _applicationLifetime.NotifyStarted();
logger.Started(); _logger.Started();
return new Disposable(() =>
{
logger.Shutdown();
_applicationLifetime.StopApplication();
Server.Dispose();
_applicationLifetime.NotifyStopped();
(_applicationServices as IDisposable)?.Dispose();
});
} }
private void EnsureApplicationServices() private void EnsureApplicationServices()
@ -248,6 +240,15 @@ namespace Microsoft.AspNet.Hosting.Internal
} }
} }
public void Dispose()
{
_logger?.Shutdown();
_applicationLifetime.StopApplication();
Server?.Dispose();
_applicationLifetime.NotifyStopped();
(_applicationServices as IDisposable)?.Dispose();
}
private class Disposable : IDisposable private class Disposable : IDisposable
{ {
private Action _dispose; private Action _dispose;

View File

@ -27,6 +27,7 @@ namespace Microsoft.AspNet.Hosting.Internal
Environment = configuration[WebApplicationConfiguration.EnvironmentKey] ?? configuration[OldEnvironmentKey]; Environment = configuration[WebApplicationConfiguration.EnvironmentKey] ?? configuration[OldEnvironmentKey];
ServerFactoryLocation = configuration[WebApplicationConfiguration.ServerKey]; ServerFactoryLocation = configuration[WebApplicationConfiguration.ServerKey];
WebRoot = configuration[WebApplicationConfiguration.WebRootKey]; WebRoot = configuration[WebApplicationConfiguration.WebRootKey];
ApplicationBasePath = configuration[WebApplicationConfiguration.ApplicationBaseKey];
} }
public string Application { get; set; } public string Application { get; set; }
@ -41,6 +42,8 @@ namespace Microsoft.AspNet.Hosting.Internal
public string WebRoot { get; set; } public string WebRoot { get; set; }
public string ApplicationBasePath { get; set; }
private static bool ParseBool(IConfiguration configuration, string key) private static bool ParseBool(IConfiguration configuration, string key)
{ {
return string.Equals("true", configuration[key], StringComparison.OrdinalIgnoreCase) return string.Equals("true", configuration[key], StringComparison.OrdinalIgnoreCase)

View File

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System; using System;
using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Runtime.Versioning; using System.Runtime.Versioning;
using Microsoft.AspNet.Builder; using Microsoft.AspNet.Builder;
@ -16,10 +17,11 @@ using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.PlatformAbstractions; using Microsoft.Extensions.PlatformAbstractions;
using Microsoft.Extensions.Primitives;
namespace Microsoft.AspNet.Hosting namespace Microsoft.AspNet.Hosting
{ {
public class WebApplicationBuilder public class WebApplicationBuilder : IWebApplicationBuilder
{ {
private readonly IHostingEnvironment _hostingEnvironment; private readonly IHostingEnvironment _hostingEnvironment;
private readonly ILoggerFactory _loggerFactory; private readonly ILoggerFactory _loggerFactory;
@ -28,19 +30,15 @@ namespace Microsoft.AspNet.Hosting
private WebApplicationOptions _options; private WebApplicationOptions _options;
private Action<IServiceCollection> _configureServices; private Action<IServiceCollection> _configureServices;
private string _environmentName;
private string _webRoot;
private string _applicationBasePath;
// Only one of these should be set // Only one of these should be set
private StartupMethods _startup; private StartupMethods _startup;
private Type _startupType; private Type _startupType;
private string _startupAssemblyName;
// Only one of these should be set // Only one of these should be set
private string _serverFactoryLocation;
private IServerFactory _serverFactory; private IServerFactory _serverFactory;
private IServer _server;
private Dictionary<string, string> _settings = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
public WebApplicationBuilder() public WebApplicationBuilder()
{ {
@ -48,62 +46,19 @@ namespace Microsoft.AspNet.Hosting
_loggerFactory = new LoggerFactory(); _loggerFactory = new LoggerFactory();
} }
public WebApplicationBuilder UseConfiguration(IConfiguration configuration) public IWebApplicationBuilder UseSetting(string key, string value)
{
_settings[key] = value;
return this;
}
public IWebApplicationBuilder UseConfiguration(IConfiguration configuration)
{ {
_config = configuration; _config = configuration;
return this; return this;
} }
public WebApplicationBuilder UseApplicationBasePath(string applicationBasePath) public IWebApplicationBuilder UseServer(IServerFactory factory)
{
_applicationBasePath = applicationBasePath;
return this;
}
public WebApplicationBuilder UseEnvironment(string environment)
{
if (environment == null)
{
throw new ArgumentNullException(nameof(environment));
}
_environmentName = environment;
return this;
}
public WebApplicationBuilder UseWebRoot(string webRoot)
{
if (webRoot == null)
{
throw new ArgumentNullException(nameof(webRoot));
}
_webRoot = webRoot;
return this;
}
public WebApplicationBuilder UseServer(IServer server)
{
if (server == null)
{
throw new ArgumentNullException(nameof(server));
}
_server = server;
return this;
}
public WebApplicationBuilder UseServerFactory(string assemblyName)
{
if (assemblyName == null)
{
throw new ArgumentNullException(nameof(assemblyName));
}
_serverFactoryLocation = assemblyName;
return this;
}
public WebApplicationBuilder UseServerFactory(IServerFactory factory)
{ {
if (factory == null) if (factory == null)
{ {
@ -114,18 +69,7 @@ namespace Microsoft.AspNet.Hosting
return this; return this;
} }
public WebApplicationBuilder UseStartup(string startupAssemblyName) public IWebApplicationBuilder UseStartup(Type startupType)
{
if (startupAssemblyName == null)
{
throw new ArgumentNullException(nameof(startupAssemblyName));
}
_startupAssemblyName = startupAssemblyName;
return this;
}
public WebApplicationBuilder UseStartup(Type startupType)
{ {
if (startupType == null) if (startupType == null)
{ {
@ -136,18 +80,13 @@ namespace Microsoft.AspNet.Hosting
return this; return this;
} }
public WebApplicationBuilder UseStartup<TStartup>() where TStartup : class public IWebApplicationBuilder ConfigureServices(Action<IServiceCollection> configureServices)
{
return UseStartup(typeof(TStartup));
}
public WebApplicationBuilder ConfigureServices(Action<IServiceCollection> configureServices)
{ {
_configureServices = configureServices; _configureServices = configureServices;
return this; return this;
} }
public WebApplicationBuilder Configure(Action<IApplicationBuilder> configureApp) public IWebApplicationBuilder Configure(Action<IApplicationBuilder> configureApp)
{ {
if (configureApp == null) if (configureApp == null)
{ {
@ -158,7 +97,7 @@ namespace Microsoft.AspNet.Hosting
return this; return this;
} }
public WebApplicationBuilder ConfigureLogging(Action<ILoggerFactory> configureLogging) public IWebApplicationBuilder ConfigureLogging(Action<ILoggerFactory> configureLogging)
{ {
configureLogging(_loggerFactory); configureLogging(_loggerFactory);
return this; return this;
@ -173,29 +112,19 @@ namespace Microsoft.AspNet.Hosting
var appEnvironment = hostingContainer.GetRequiredService<IApplicationEnvironment>(); var appEnvironment = hostingContainer.GetRequiredService<IApplicationEnvironment>();
var startupLoader = hostingContainer.GetRequiredService<IStartupLoader>(); var startupLoader = hostingContainer.GetRequiredService<IStartupLoader>();
_config = _config ?? WebApplicationConfiguration.GetDefault();
_options = new WebApplicationOptions(_config);
// Initialize the hosting environment // Initialize the hosting environment
_options.WebRoot = _webRoot ?? _options.WebRoot;
_hostingEnvironment.Initialize(appEnvironment.ApplicationBasePath, _options, _config); _hostingEnvironment.Initialize(appEnvironment.ApplicationBasePath, _options, _config);
if (!string.IsNullOrEmpty(_environmentName))
{
_hostingEnvironment.EnvironmentName = _environmentName;
}
var application = new WebApplication(hostingServices, startupLoader, _options, _config); var application = new WebApplication(hostingServices, startupLoader, _options, _config);
// Only one of these should be set, but they are used in priority // Only one of these should be set, but they are used in priority
application.Server = _server;
application.ServerFactory = _serverFactory; application.ServerFactory = _serverFactory;
application.ServerFactoryLocation = _options.ServerFactoryLocation ?? _serverFactoryLocation; application.ServerFactoryLocation = _options.ServerFactoryLocation;
// Only one of these should be set, but they are used in priority // Only one of these should be set, but they are used in priority
application.Startup = _startup; application.Startup = _startup;
application.StartupType = _startupType; application.StartupType = _startupType;
application.StartupAssemblyName = _startupAssemblyName ?? _options.Application; application.StartupAssemblyName = _options.Application;
application.Initialize(); application.Initialize();
@ -204,6 +133,17 @@ namespace Microsoft.AspNet.Hosting
private IServiceCollection BuildHostingServices() private IServiceCollection BuildHostingServices()
{ {
// Apply the configuration settings
var configuration = _config ?? WebApplicationConfiguration.GetDefault();
var mergedConfiguration = new ConfigurationBuilder()
.Add(new IncludedConfigurationProvider(configuration))
.AddInMemoryCollection(_settings)
.Build();
_config = mergedConfiguration;
_options = new WebApplicationOptions(_config);
var services = new ServiceCollection(); var services = new ServiceCollection();
services.AddSingleton(_hostingEnvironment); services.AddSingleton(_hostingEnvironment);
services.AddSingleton(_loggerFactory); services.AddSingleton(_loggerFactory);
@ -230,9 +170,9 @@ namespace Microsoft.AspNet.Hosting
if (defaultPlatformServices.Application != null) if (defaultPlatformServices.Application != null)
{ {
var appEnv = defaultPlatformServices.Application; var appEnv = defaultPlatformServices.Application;
if (!string.IsNullOrEmpty(_applicationBasePath)) if (!string.IsNullOrEmpty(_options.ApplicationBasePath))
{ {
appEnv = new WrappedApplicationEnvironment(_applicationBasePath, appEnv); appEnv = new WrappedApplicationEnvironment(_options.ApplicationBasePath, appEnv);
} }
services.TryAddSingleton(appEnv); services.TryAddSingleton(appEnv);

View File

@ -0,0 +1,148 @@
// 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;
using Microsoft.AspNet.Hosting.Server;
using Microsoft.AspNet.Server.Features;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
namespace Microsoft.AspNet.Hosting
{
public static class WebApplicationBuilderExtensions
{
private static readonly string ServerUrlsSeparator = ";";
public static IWebApplicationBuilder UseStartup<TStartup>(this IWebApplicationBuilder applicationBuilder) where TStartup : class
{
return applicationBuilder.UseStartup(typeof(TStartup));
}
public static IWebApplicationBuilder UseServer(this IWebApplicationBuilder applicationBuilder, string assemblyName)
{
if (assemblyName == null)
{
throw new ArgumentNullException(nameof(assemblyName));
}
return applicationBuilder.UseSetting(WebApplicationConfiguration.ServerKey, assemblyName);
}
public static IWebApplicationBuilder UseServer(this IWebApplicationBuilder applicationBuilder, IServer server)
{
if (server == null)
{
throw new ArgumentNullException(nameof(server));
}
return applicationBuilder.UseServer(new ServerFactory(server));
}
public static IWebApplicationBuilder UseApplicationBasePath(this IWebApplicationBuilder applicationBuilder, string applicationBasePath)
{
if (applicationBasePath == null)
{
throw new ArgumentNullException(nameof(applicationBasePath));
}
return applicationBuilder.UseSetting(WebApplicationConfiguration.ApplicationBaseKey, applicationBasePath);
}
public static IWebApplicationBuilder UseEnvironment(this IWebApplicationBuilder applicationBuilder, string environment)
{
if (environment == null)
{
throw new ArgumentNullException(nameof(environment));
}
return applicationBuilder.UseSetting(WebApplicationConfiguration.EnvironmentKey, environment);
}
public static IWebApplicationBuilder UseWebRoot(this IWebApplicationBuilder applicationBuilder, string webRoot)
{
if (webRoot == null)
{
throw new ArgumentNullException(nameof(webRoot));
}
return applicationBuilder.UseSetting(WebApplicationConfiguration.WebRootKey, webRoot);
}
public static IWebApplicationBuilder UseUrls(this IWebApplicationBuilder applicationBuilder, params string[] urls)
{
if (urls == null)
{
throw new ArgumentNullException(nameof(urls));
}
return applicationBuilder.UseSetting(WebApplicationConfiguration.ServerUrlsKey, string.Join(ServerUrlsSeparator, urls));
}
public static IWebApplicationBuilder UseStartup(this IWebApplicationBuilder applicationBuilder, string startupAssemblyName)
{
if (startupAssemblyName == null)
{
throw new ArgumentNullException(nameof(startupAssemblyName));
}
return applicationBuilder.UseSetting(WebApplicationConfiguration.ApplicationKey, startupAssemblyName);
}
public static IWebApplication Start(this IWebApplicationBuilder applicationBuilder, params string[] urls)
{
var application = applicationBuilder.UseUrls(urls).Build();
application.Start();
return application;
}
/// <summary>
/// Runs a web application and block the calling thread until host shutdown.
/// </summary>
/// <param name="application"></param>
public static void Run(this IWebApplication application)
{
using (application)
{
application.Start();
var hostingEnvironment = application.Services.GetService<IHostingEnvironment>();
var applicationLifetime = application.Services.GetService<IApplicationLifetime>();
Console.WriteLine("Hosting environment: " + hostingEnvironment.EnvironmentName);
var serverAddresses = application.ServerFeatures.Get<IServerAddressesFeature>()?.Addresses;
if (serverAddresses != null)
{
foreach (var address in serverAddresses)
{
Console.WriteLine("Now listening on: " + address);
}
}
Console.WriteLine("Application started. Press Ctrl+C to shut down.");
Console.CancelKeyPress += (sender, eventArgs) =>
{
applicationLifetime.StopApplication();
// Don't terminate the process immediately, wait for the Main thread to exit gracefully.
eventArgs.Cancel = true;
};
applicationLifetime.ApplicationStopping.WaitHandle.WaitOne();
}
}
private class ServerFactory : IServerFactory
{
private readonly IServer _server;
public ServerFactory(IServer server)
{
_server = server;
}
public IServer CreateServer(IConfiguration configuration) => _server;
}
}
}

View File

@ -4,12 +4,14 @@ namespace Microsoft.AspNet.Hosting
{ {
public class WebApplicationConfiguration public class WebApplicationConfiguration
{ {
public static readonly string ApplicationKey = "app"; public static readonly string ApplicationKey = "application";
public static readonly string DetailedErrorsKey = "detailedErrors"; public static readonly string DetailedErrorsKey = "detailedErrors";
public static readonly string EnvironmentKey = "environment"; public static readonly string EnvironmentKey = "environment";
public static readonly string ServerKey = "server"; public static readonly string ServerKey = "server";
public static readonly string WebRootKey = "webroot"; public static readonly string WebRootKey = "webroot";
public static readonly string CaptureStartupErrorsKey = "captureStartupErrors"; public static readonly string CaptureStartupErrorsKey = "captureStartupErrors";
public static readonly string ServerUrlsKey = "server.urls";
public static readonly string ApplicationBaseKey = "applicationBase";
public static readonly string HostingJsonFile = "hosting.json"; public static readonly string HostingJsonFile = "hosting.json";
public static readonly string EnvironmentVariablesPrefix = "ASPNET_"; public static readonly string EnvironmentVariablesPrefix = "ASPNET_";

View File

@ -1,60 +0,0 @@
// 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;
using System.Collections.Generic;
using Microsoft.AspNet.Http.Features;
using Microsoft.AspNet.Server.Features;
using Microsoft.Extensions.DependencyInjection;
namespace Microsoft.AspNet.Hosting
{
public static class WebApplicationExtensions
{
/// <summary>
/// Retruns the server addresses the web application is going to listen on.
/// </summary>
/// <param name="application"></param>
/// <returns>An <see cref="ICollection{string}"> which the addresses the server will listen to</returns>
public static ICollection<string> GetAddresses(this IWebApplication application)
{
return application.ServerFeatures.Get<IServerAddressesFeature>()?.Addresses;
}
/// <summary>
/// Runs a web application and block the calling thread until host shutdown.
/// </summary>
/// <param name="application"></param>
public static void Run(this IWebApplication application)
{
using (application.Start())
{
var hostingEnvironment = application.Services.GetService<IHostingEnvironment>();
var applicationLifetime = application.Services.GetService<IApplicationLifetime>();
Console.WriteLine("Hosting environment: " + hostingEnvironment.EnvironmentName);
var serverAddresses = application.GetAddresses();
if (serverAddresses != null)
{
foreach (var address in serverAddresses)
{
Console.WriteLine("Now listening on: " + address);
}
}
Console.WriteLine("Application started. Press Ctrl+C to shut down.");
Console.CancelKeyPress += (sender, eventArgs) =>
{
applicationLifetime.StopApplication();
// Don't terminate the process immediately, wait for the Main thread to exit gracefully.
eventArgs.Cancel = true;
};
applicationLifetime.ApplicationStopping.WaitHandle.WaitOne();
}
}
}
}

View File

@ -16,14 +16,15 @@ namespace Microsoft.AspNet.TestHost
{ {
private const string DefaultEnvironmentName = "Development"; private const string DefaultEnvironmentName = "Development";
private const string ServerName = nameof(TestServer); private const string ServerName = nameof(TestServer);
private IDisposable _appInstance; private IWebApplication _appInstance;
private bool _disposed = false; private bool _disposed = false;
private IHttpApplication<Context> _application; private IHttpApplication<Context> _application;
public TestServer(WebApplicationBuilder builder) public TestServer(IWebApplicationBuilder builder)
{ {
var application = builder.UseServer(this).Build(); var application = builder.UseServer(this).Build();
_appInstance = application.Start(); application.Start();
_appInstance = application;
} }
public Uri BaseAddress { get; set; } = new Uri("http://localhost/"); public Uri BaseAddress { get; set; } = new Uri("http://localhost/");
@ -59,8 +60,11 @@ namespace Microsoft.AspNet.TestHost
public void Dispose() public void Dispose()
{ {
_disposed = true; if (!_disposed)
_appInstance.Dispose(); {
_disposed = true;
_appInstance.Dispose();
}
} }
void IServer.Start<TContext>(IHttpApplication<TContext> application) void IServer.Start<TContext>(IHttpApplication<TContext> application)

View File

@ -36,8 +36,9 @@ namespace Microsoft.AspNet.Hosting
var builder = CreateWebApplicationBuilder(); var builder = CreateWebApplicationBuilder();
var server = new TestServer(); var server = new TestServer();
var application = builder.UseServer(server).UseStartup("MissingStartupAssembly").Build(); var application = builder.UseServer(server).UseStartup("MissingStartupAssembly").Build();
using (application.Start()) using (application)
{ {
application.Start();
await AssertResponseContains(server.RequestDelegate, "MissingStartupAssembly"); await AssertResponseContains(server.RequestDelegate, "MissingStartupAssembly");
} }
} }
@ -48,8 +49,9 @@ namespace Microsoft.AspNet.Hosting
var builder = CreateWebApplicationBuilder(); var builder = CreateWebApplicationBuilder();
var server = new TestServer(); var server = new TestServer();
var application = builder.UseServer(server).UseStartup<StartupStaticCtorThrows>().Build(); var application = builder.UseServer(server).UseStartup<StartupStaticCtorThrows>().Build();
using (application.Start()) using (application)
{ {
application.Start();
await AssertResponseContains(server.RequestDelegate, "Exception from static constructor"); await AssertResponseContains(server.RequestDelegate, "Exception from static constructor");
} }
} }
@ -60,8 +62,9 @@ namespace Microsoft.AspNet.Hosting
var builder = CreateWebApplicationBuilder(); var builder = CreateWebApplicationBuilder();
var server = new TestServer(); var server = new TestServer();
var application = builder.UseServer(server).UseStartup<StartupCtorThrows>().Build(); var application = builder.UseServer(server).UseStartup<StartupCtorThrows>().Build();
using (application.Start()) using (application)
{ {
application.Start();
await AssertResponseContains(server.RequestDelegate, "Exception from constructor"); await AssertResponseContains(server.RequestDelegate, "Exception from constructor");
} }
} }
@ -72,8 +75,9 @@ namespace Microsoft.AspNet.Hosting
var builder = CreateWebApplicationBuilder(); var builder = CreateWebApplicationBuilder();
var server = new TestServer(); var server = new TestServer();
var application = builder.UseServer(server).UseStartup<StartupThrowTypeLoadException>().Build(); var application = builder.UseServer(server).UseStartup<StartupThrowTypeLoadException>().Build();
using (application.Start()) using (application)
{ {
application.Start();
await AssertResponseContains(server.RequestDelegate, "Message from the LoaderException</span>"); await AssertResponseContains(server.RequestDelegate, "Message from the LoaderException</span>");
} }
} }
@ -84,8 +88,9 @@ namespace Microsoft.AspNet.Hosting
var builder = CreateWebApplicationBuilder(); var builder = CreateWebApplicationBuilder();
var server = new TestServer(); var server = new TestServer();
var application = builder.UseServer(server).UseStartup<StartupCtorThrows>().Build(); var application = builder.UseServer(server).UseStartup<StartupCtorThrows>().Build();
using (application.Start()) using (application)
{ {
application.Start();
var service = application.Services.GetServices<IApplicationLifetime>(); var service = application.Services.GetServices<IApplicationLifetime>();
Assert.NotNull(service); Assert.NotNull(service);
await AssertResponseContains(server.RequestDelegate, "Exception from constructor"); await AssertResponseContains(server.RequestDelegate, "Exception from constructor");
@ -98,8 +103,9 @@ namespace Microsoft.AspNet.Hosting
var builder = CreateWebApplicationBuilder(); var builder = CreateWebApplicationBuilder();
var server = new TestServer(); var server = new TestServer();
var application = builder.UseServer(server).UseStartup<StartupConfigureServicesThrows>().Build(); var application = builder.UseServer(server).UseStartup<StartupConfigureServicesThrows>().Build();
using (application.Start()) using (application)
{ {
application.Start();
await AssertResponseContains(server.RequestDelegate, "Exception from ConfigureServices"); await AssertResponseContains(server.RequestDelegate, "Exception from ConfigureServices");
} }
} }
@ -110,8 +116,9 @@ namespace Microsoft.AspNet.Hosting
var builder = CreateWebApplicationBuilder(); var builder = CreateWebApplicationBuilder();
var server = new TestServer(); var server = new TestServer();
var application = builder.UseServer(server).UseStartup<StartupConfigureServicesThrows>().Build(); var application = builder.UseServer(server).UseStartup<StartupConfigureServicesThrows>().Build();
using (application.Start()) using (application)
{ {
application.Start();
await AssertResponseContains(server.RequestDelegate, "Exception from Configure"); await AssertResponseContains(server.RequestDelegate, "Exception from Configure");
} }
} }
@ -138,6 +145,28 @@ namespace Microsoft.AspNet.Hosting
Assert.Equal(expected, application.Services.GetService<IHostingEnvironment>().EnvironmentName); Assert.Equal(expected, application.Services.GetService<IHostingEnvironment>().EnvironmentName);
} }
[Fact]
public void BuildAndDispose()
{
var vals = new Dictionary<string, string>
{
{ "ENV", "Dev" },
};
var builder = new ConfigurationBuilder()
.AddInMemoryCollection(vals);
var config = builder.Build();
var expected = "MY_TEST_ENVIRONMENT";
var application = new WebApplicationBuilder()
.UseConfiguration(config)
.UseEnvironment(expected)
.UseServer(new TestServer())
.UseStartup("Microsoft.AspNet.Hosting.Tests")
.Build();
application.Dispose();
}
[Fact] [Fact]
public void UseBasePathConfiguresBasePath() public void UseBasePathConfiguresBasePath()
{ {
@ -159,7 +188,7 @@ namespace Microsoft.AspNet.Hosting
Assert.Equal("/foo/bar", application.Services.GetService<IApplicationEnvironment>().ApplicationBasePath); Assert.Equal("/foo/bar", application.Services.GetService<IApplicationEnvironment>().ApplicationBasePath);
} }
private WebApplicationBuilder CreateWebApplicationBuilder() private IWebApplicationBuilder CreateWebApplicationBuilder()
{ {
var vals = new Dictionary<string, string> var vals = new Dictionary<string, string>
{ {

View File

@ -15,11 +15,11 @@ namespace Microsoft.AspNet.Hosting.Tests
{ {
var parameters = new Dictionary<string, string>() var parameters = new Dictionary<string, string>()
{ {
{"webroot", "wwwroot"}, { "webroot", "wwwroot"},
{"server", "Microsoft.AspNet.Server.Kestrel"}, { "server", "Microsoft.AspNet.Server.Kestrel"},
{"app", "MyProjectReference"}, { "application", "MyProjectReference"},
{"environment", "Development"}, { "environment", "Development"},
{"detailederrors", "true"}, { "detailederrors", "true"},
{ "captureStartupErrors", "true" } { "captureStartupErrors", "true" }
}; };

View File

@ -119,7 +119,7 @@ namespace Microsoft.AspNet.Hosting
.AddInMemoryCollection(vals); .AddInMemoryCollection(vals);
var config = builder.Build(); var config = builder.Build();
var application = CreateBuilder(config).Build(); var application = CreateBuilder(config).Build();
var app = application.Start(); application.Start();
Assert.NotNull(application.Services.GetService<IHostingEnvironment>()); Assert.NotNull(application.Services.GetService<IHostingEnvironment>());
Assert.Equal("http://localhost:abc123", application.ServerFeatures.Get<IServerAddressesFeature>().Addresses.First()); Assert.Equal("http://localhost:abc123", application.ServerFeatures.Get<IServerAddressesFeature>().Addresses.First());
} }
@ -136,9 +136,9 @@ namespace Microsoft.AspNet.Hosting
.AddInMemoryCollection(vals); .AddInMemoryCollection(vals);
var config = builder.Build(); var config = builder.Build();
var application = CreateBuilder(config).Build(); var application = CreateBuilder(config).Build();
var app = application.Start(); application.Start();
Assert.NotNull(application.Services.GetService<IHostingEnvironment>()); Assert.NotNull(application.Services.GetService<IHostingEnvironment>());
Assert.Equal("http://localhost:5000", application.GetAddresses().First()); Assert.Equal("http://localhost:5000", application.ServerFeatures.Get<IServerAddressesFeature>().Addresses.First());
} }
[Fact] [Fact]
@ -153,7 +153,7 @@ namespace Microsoft.AspNet.Hosting
.AddInMemoryCollection(vals); .AddInMemoryCollection(vals);
var config = builder.Build(); var config = builder.Build();
var application = CreateBuilder(config).Build(); var application = CreateBuilder(config).Build();
var app = application.Start(); application.Start();
var hostingEnvironment = application.Services.GetService<IHostingEnvironment>(); var hostingEnvironment = application.Services.GetService<IHostingEnvironment>();
Assert.NotNull(hostingEnvironment.Configuration); Assert.NotNull(hostingEnvironment.Configuration);
Assert.Equal("Microsoft.AspNet.Hosting.Tests", hostingEnvironment.Configuration["Server"]); Assert.Equal("Microsoft.AspNet.Hosting.Tests", hostingEnvironment.Configuration["Server"]);
@ -163,9 +163,8 @@ namespace Microsoft.AspNet.Hosting
public void WebApplicationCanBeStarted() public void WebApplicationCanBeStarted()
{ {
var app = CreateBuilder() var app = CreateBuilder()
.UseServer(this) .UseServer((IServerFactory)this)
.UseStartup("Microsoft.AspNet.Hosting.Tests") .UseStartup("Microsoft.AspNet.Hosting.Tests")
.Build()
.Start(); .Start();
Assert.NotNull(app); Assert.NotNull(app);
@ -181,7 +180,7 @@ namespace Microsoft.AspNet.Hosting
public void WebApplicationDisposesServiceProvider() public void WebApplicationDisposesServiceProvider()
{ {
var application = CreateBuilder() var application = CreateBuilder()
.UseServer(this) .UseServer((IServerFactory)this)
.ConfigureServices(s => .ConfigureServices(s =>
{ {
s.AddTransient<IFakeService, FakeService>(); s.AddTransient<IFakeService, FakeService>();
@ -190,7 +189,7 @@ namespace Microsoft.AspNet.Hosting
.UseStartup("Microsoft.AspNet.Hosting.Tests") .UseStartup("Microsoft.AspNet.Hosting.Tests")
.Build(); .Build();
var app = application.Start(); application.Start();
var singleton = (FakeService)application.Services.GetService<IFakeSingletonService>(); var singleton = (FakeService)application.Services.GetService<IFakeSingletonService>();
var transient = (FakeService)application.Services.GetService<IFakeService>(); var transient = (FakeService)application.Services.GetService<IFakeService>();
@ -198,7 +197,7 @@ namespace Microsoft.AspNet.Hosting
Assert.False(singleton.Disposed); Assert.False(singleton.Disposed);
Assert.False(transient.Disposed); Assert.False(transient.Disposed);
app.Dispose(); application.Dispose();
Assert.True(singleton.Disposed); Assert.True(singleton.Disposed);
Assert.True(transient.Disposed); Assert.True(transient.Disposed);
@ -208,13 +207,14 @@ namespace Microsoft.AspNet.Hosting
public void WebApplicationNotifiesApplicationStarted() public void WebApplicationNotifiesApplicationStarted()
{ {
var application = CreateBuilder() var application = CreateBuilder()
.UseServer(this) .UseServer((IServerFactory)this)
.Build(); .Build();
var applicationLifetime = application.Services.GetService<IApplicationLifetime>(); var applicationLifetime = application.Services.GetService<IApplicationLifetime>();
Assert.False(applicationLifetime.ApplicationStarted.IsCancellationRequested); Assert.False(applicationLifetime.ApplicationStarted.IsCancellationRequested);
using (application.Start()) using (application)
{ {
application.Start();
Assert.True(applicationLifetime.ApplicationStarted.IsCancellationRequested); Assert.True(applicationLifetime.ApplicationStarted.IsCancellationRequested);
} }
} }
@ -223,13 +223,14 @@ namespace Microsoft.AspNet.Hosting
public void WebApplicationInjectsHostingEnvironment() public void WebApplicationInjectsHostingEnvironment()
{ {
var application = CreateBuilder() var application = CreateBuilder()
.UseServer(this) .UseServer((IServerFactory)this)
.UseStartup("Microsoft.AspNet.Hosting.Tests") .UseStartup("Microsoft.AspNet.Hosting.Tests")
.UseEnvironment("WithHostingEnvironment") .UseEnvironment("WithHostingEnvironment")
.Build(); .Build();
using (application.Start()) using (application)
{ {
application.Start();
var env = application.Services.GetService<IHostingEnvironment>(); var env = application.Services.GetService<IHostingEnvironment>();
Assert.Equal("Changed", env.EnvironmentName); Assert.Equal("Changed", env.EnvironmentName);
} }
@ -243,7 +244,7 @@ namespace Microsoft.AspNet.Hosting
{ {
services.AddTransient<IStartupLoader, TestLoader>(); services.AddTransient<IStartupLoader, TestLoader>();
}) })
.UseServer(this) .UseServer((IServerFactory)this)
.UseStartup("Microsoft.AspNet.Hosting.Tests"); .UseStartup("Microsoft.AspNet.Hosting.Tests");
Assert.Throws<NotImplementedException>(() => builder.Build()); Assert.Throws<NotImplementedException>(() => builder.Build());
@ -252,14 +253,14 @@ namespace Microsoft.AspNet.Hosting
[Fact] [Fact]
public void CanCreateApplicationServicesWithAddedServices() public void CanCreateApplicationServicesWithAddedServices()
{ {
var application = CreateBuilder().UseServer(this).ConfigureServices(services => services.AddOptions()).Build(); var application = CreateBuilder().UseServer((IServerFactory)this).ConfigureServices(services => services.AddOptions()).Build();
Assert.NotNull(application.Services.GetRequiredService<IOptions<object>>()); Assert.NotNull(application.Services.GetRequiredService<IOptions<object>>());
} }
[Fact] [Fact]
public void EnvDefaultsToProductionIfNoConfig() public void EnvDefaultsToProductionIfNoConfig()
{ {
var application = CreateBuilder().UseServer(this).Build(); var application = CreateBuilder().UseServer((IServerFactory)this).Build();
var env = application.Services.GetService<IHostingEnvironment>(); var env = application.Services.GetService<IHostingEnvironment>();
Assert.Equal(EnvironmentName.Production, env.EnvironmentName); Assert.Equal(EnvironmentName.Production, env.EnvironmentName);
} }
@ -278,7 +279,7 @@ namespace Microsoft.AspNet.Hosting
.AddInMemoryCollection(vals); .AddInMemoryCollection(vals);
var config = builder.Build(); var config = builder.Build();
var application = CreateBuilder(config).UseServer(this).Build(); var application = CreateBuilder(config).UseServer((IServerFactory)this).Build();
var env = application.Services.GetService<IHostingEnvironment>(); var env = application.Services.GetService<IHostingEnvironment>();
Assert.Equal("Staging", env.EnvironmentName); Assert.Equal("Staging", env.EnvironmentName);
} }
@ -295,7 +296,7 @@ namespace Microsoft.AspNet.Hosting
.AddInMemoryCollection(vals); .AddInMemoryCollection(vals);
var config = builder.Build(); var config = builder.Build();
var application = CreateBuilder(config).UseServer(this).Build(); var application = CreateBuilder(config).UseServer((IServerFactory)this).Build();
var env = application.Services.GetService<IHostingEnvironment>(); var env = application.Services.GetService<IHostingEnvironment>();
Assert.Equal("Staging", env.EnvironmentName); Assert.Equal("Staging", env.EnvironmentName);
} }
@ -312,7 +313,7 @@ namespace Microsoft.AspNet.Hosting
.AddInMemoryCollection(vals); .AddInMemoryCollection(vals);
var config = builder.Build(); var config = builder.Build();
var application = CreateBuilder(config).UseServer(this).Build(); var application = CreateBuilder(config).UseServer((IServerFactory)this).Build();
var env = application.Services.GetService<IHostingEnvironment>(); var env = application.Services.GetService<IHostingEnvironment>();
Assert.Equal(Path.GetFullPath("testroot"), env.WebRootPath); Assert.Equal(Path.GetFullPath("testroot"), env.WebRootPath);
Assert.True(env.WebRootFileProvider.GetFileInfo("TextFile.txt").Exists); Assert.True(env.WebRootFileProvider.GetFileInfo("TextFile.txt").Exists);
@ -321,9 +322,10 @@ namespace Microsoft.AspNet.Hosting
[Fact] [Fact]
public void IsEnvironment_Extension_Is_Case_Insensitive() public void IsEnvironment_Extension_Is_Case_Insensitive()
{ {
var application = CreateBuilder().UseServer(this).Build(); var application = CreateBuilder().UseServer((IServerFactory)this).Build();
using (application.Start()) using (application)
{ {
application.Start();
var env = application.Services.GetRequiredService<IHostingEnvironment>(); var env = application.Services.GetRequiredService<IHostingEnvironment>();
Assert.True(env.IsEnvironment(EnvironmentName.Production)); Assert.True(env.IsEnvironment(EnvironmentName.Production));
Assert.True(env.IsEnvironment("producTion")); Assert.True(env.IsEnvironment("producTion"));
@ -364,7 +366,7 @@ namespace Microsoft.AspNet.Hosting
var application = CreateApplication(requestDelegate); var application = CreateApplication(requestDelegate);
// Act // Act
var disposable = application.Start(); application.Start();
// Assert // Assert
Assert.NotNull(httpContext); Assert.NotNull(httpContext);
@ -388,7 +390,7 @@ namespace Microsoft.AspNet.Hosting
var application = CreateApplication(requestDelegate); var application = CreateApplication(requestDelegate);
// Act // Act
var disposable = application.Start(); application.Start();
// Assert // Assert
Assert.NotNull(httpContext); Assert.NotNull(httpContext);
@ -399,11 +401,12 @@ namespace Microsoft.AspNet.Hosting
public void WebApplication_InvokesConfigureMethodsOnlyOnce() public void WebApplication_InvokesConfigureMethodsOnlyOnce()
{ {
var application = CreateBuilder() var application = CreateBuilder()
.UseServer(this) .UseServer((IServerFactory)this)
.UseStartup<CountStartup>() .UseStartup<CountStartup>()
.Build(); .Build();
using (application.Start()) using (application)
{ {
application.Start();
var services = application.Services; var services = application.Services;
var services2 = application.Services; var services2 = application.Services;
Assert.Equal(1, CountStartup.ConfigureCount); Assert.Equal(1, CountStartup.ConfigureCount);
@ -431,7 +434,7 @@ namespace Microsoft.AspNet.Hosting
public void WebApplication_ThrowsForBadConfigureServiceSignature() public void WebApplication_ThrowsForBadConfigureServiceSignature()
{ {
var builder = CreateBuilder() var builder = CreateBuilder()
.UseServer(this) .UseServer((IServerFactory)this)
.UseStartup<BadConfigureServicesStartup>(); .UseStartup<BadConfigureServicesStartup>();
var ex = Assert.Throws<InvalidOperationException>(() => builder.Build()); var ex = Assert.Throws<InvalidOperationException>(() => builder.Build());
@ -447,7 +450,7 @@ namespace Microsoft.AspNet.Hosting
private IWebApplication CreateApplication(RequestDelegate requestDelegate) private IWebApplication CreateApplication(RequestDelegate requestDelegate)
{ {
var builder = CreateBuilder() var builder = CreateBuilder()
.UseServer(this) .UseServer((IServerFactory)this)
.Configure( .Configure(
appBuilder => appBuilder =>
{ {
@ -459,10 +462,11 @@ namespace Microsoft.AspNet.Hosting
private void RunMapPath(string virtualPath, string expectedSuffix) private void RunMapPath(string virtualPath, string expectedSuffix)
{ {
var application = CreateBuilder().UseServer(this).Build(); var application = CreateBuilder().UseServer((IServerFactory)this).Build();
using (application.Start()) using (application)
{ {
application.Start();
var env = application.Services.GetRequiredService<IHostingEnvironment>(); var env = application.Services.GetRequiredService<IHostingEnvironment>();
// MapPath requires webroot to be set, we don't care // MapPath requires webroot to be set, we don't care
// about file provider so just set it here // about file provider so just set it here
@ -473,7 +477,7 @@ namespace Microsoft.AspNet.Hosting
} }
} }
private WebApplicationBuilder CreateBuilder(IConfiguration config = null) private IWebApplicationBuilder CreateBuilder(IConfiguration config = null)
{ {
return new WebApplicationBuilder().UseConfiguration(config ?? new ConfigurationBuilder().Build()).UseStartup("Microsoft.AspNet.Hosting.Tests"); return new WebApplicationBuilder().UseConfiguration(config ?? new ConfigurationBuilder().Build()).UseStartup("Microsoft.AspNet.Hosting.Tests");
} }