Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Minimal hosting #31825

Merged
merged 25 commits into from
Apr 21, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
dbbe9fa
Port WebApplication WIP
halter73 Apr 14, 2021
2858a94
Remove private copy of Host's ConfigureDefaults
halter73 Apr 15, 2021
def631b
Add sample. "Fix" startup.
halter73 Apr 15, 2021
c9b9432
Add IAsyncDisposable support
halter73 Apr 15, 2021
dbf2382
Add unit tests and make IServer replaceable
halter73 Apr 15, 2021
c53f5a9
Hide WebApplication.Dispose in favor of DisposeAsync
halter73 Apr 15, 2021
3cc8665
seal WebApplication and WebApplicationBuilder
halter73 Apr 15, 2021
cae4c27
Add logging config functional test
halter73 Apr 15, 2021
14d6d72
Update MinimalSample to use WebApplication!!!
halter73 Apr 15, 2021
9cebfd1
Seal Configuration and add WebApplicationBuilder_CanClearDefaultLogge…
halter73 Apr 15, 2021
dcb450d
Explain why it's OK to noop in ConfigurationHostBuilder
halter73 Apr 15, 2021
abc566b
Match the proposed API
halter73 Apr 16, 2021
30d67c9
Make Configure(Web)?HostBuilder public
halter73 Apr 16, 2021
a02158b
missed one
halter73 Apr 16, 2021
5e11585
Fix host builder case sensitivity and add tests.
halter73 Apr 16, 2021
6da3d33
"NotFound" -> string.Empty
halter73 Apr 16, 2021
6af9e02
Address PR feedback
halter73 Apr 17, 2021
ebbda68
Fix handling of ASPNETCORE_ environment variables
halter73 Apr 17, 2021
8aeaa4a
Add WebHostEnvironment.ApplyConfigurationSettings
halter73 Apr 17, 2021
1184f79
cleanup
halter73 Apr 17, 2021
b430995
React to API review feedback
halter73 Apr 19, 2021
ed47002
Addresses IEnumerable<string> -> ICollection<string>
halter73 Apr 19, 2021
c9f8fdc
Addresses -> Urls
halter73 Apr 19, 2021
a1136f7
Make Urls non-nullable
halter73 Apr 19, 2021
bae765b
Add more tests
halter73 Apr 20, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 0 additions & 6 deletions src/DefaultBuilder/DefaultBuilder.slnf
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,6 @@
"src\\DefaultBuilder\\samples\\SampleApp\\DefaultBuilder.SampleApp.csproj",
"src\\DefaultBuilder\\test\\Microsoft.AspNetCore.Tests\\Microsoft.AspNetCore.Tests.csproj",
"src\\DefaultBuilder\\test\\Microsoft.AspNetCore.FunctionalTests\\Microsoft.AspNetCore.FunctionalTests.csproj",
"src\\DefaultBuilder\\testassets\\CreateDefaultBuilderApp\\CreateDefaultBuilderApp.csproj",
"src\\DefaultBuilder\\testassets\\CreateDefaultBuilderOfTApp\\CreateDefaultBuilderOfTApp.csproj",
"src\\DefaultBuilder\\testassets\\DependencyInjectionApp\\DependencyInjectionApp.csproj",
"src\\DefaultBuilder\\testassets\\StartRequestDelegateUrlApp\\StartRequestDelegateUrlApp.csproj",
"src\\DefaultBuilder\\testassets\\StartRouteBuilderUrlApp\\StartRouteBuilderUrlApp.csproj",
"src\\DefaultBuilder\\testassets\\StartWithIApplicationBuilderUrlApp\\StartWithIApplicationBuilderUrlApp.csproj",
"src\\DefaultBuilder\\src\\Microsoft.AspNetCore.csproj",
"src\\Hosting\\Server.IntegrationTesting\\src\\Microsoft.AspNetCore.Server.IntegrationTesting.csproj",
"src\\Middleware\\StaticFiles\\src\\Microsoft.AspNetCore.StaticFiles.csproj"
Expand Down
31 changes: 20 additions & 11 deletions src/DefaultBuilder/samples/SampleApp/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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 System.Threading.Tasks;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
Expand All @@ -14,17 +15,14 @@ namespace SampleApp
{
public class Program
{
public static void Main(string[] args)
public static async Task Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
await using var webApp = WebApplication.Create(args);
halter73 marked this conversation as resolved.
Show resolved Hide resolved

public static IHostBuilder CreateHostBuilder(string[] args) =>
Tratcher marked this conversation as resolved.
Show resolved Hide resolved
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
webApp.MapGet("/", (Func<string>)(() => "Hello, World!"));
halter73 marked this conversation as resolved.
Show resolved Hide resolved

await webApp.RunAsync();
}

private static void HelloWorld()
{
Expand Down Expand Up @@ -80,8 +78,7 @@ private static void CustomApplicationBuilder()
Console.ReadKey();
}
}

private static void StartupClass(string[] args)
private static void DirectWebHost(string[] args)
{
// Using defaults with a Startup class
using (var host = WebHost.CreateDefaultBuilder(args)
Expand All @@ -107,5 +104,17 @@ private static void HostBuilderWithWebHost(string[] args)

host.Run();
}

private static void DefaultGenericHost(string[] args)
{
CreateHostBuilder(args).Build().Run();
}

public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
}
82 changes: 82 additions & 0 deletions src/DefaultBuilder/src/BootstrapHostBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// 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.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace Microsoft.AspNetCore.Hosting
{
// This exists solely to bootstrap the configuration
internal class BootstrapHostBuilder : IHostBuilder
{
public IDictionary<object, object> Properties { get; } = new Dictionary<object, object>();
private readonly HostBuilderContext _context;
private readonly Configuration _configuration;
private readonly WebHostEnvironment _environment;

public BootstrapHostBuilder(Configuration configuration, WebHostEnvironment webHostEnvironment)
{
_configuration = configuration;
_environment = webHostEnvironment;
_context = new HostBuilderContext(Properties)
{
Configuration = configuration,
HostingEnvironment = webHostEnvironment
};
}

public IHost Build()
{
// HostingHostBuilderExtensions.ConfigureDefaults should never call this.
throw new InvalidOperationException();
}

public IHostBuilder ConfigureAppConfiguration(Action<HostBuilderContext, IConfigurationBuilder> configureDelegate)
{
configureDelegate(_context, _configuration);
_environment.ApplyConfigurationSettings(_configuration);
_configuration.ChangeBasePath(_environment.ContentRootPath);
return this;
}

public IHostBuilder ConfigureContainer<TContainerBuilder>(Action<HostBuilderContext, TContainerBuilder> configureDelegate)
{
// This is not called by HostingHostBuilderExtensions.ConfigureDefaults currently, but that could change in the future.
// If this does get called in the future, it should be called again at a later stage on the ConfigureHostBuilder.
return this;
}

public IHostBuilder ConfigureHostConfiguration(Action<IConfigurationBuilder> configureDelegate)
{
configureDelegate(_configuration);
_environment.ApplyConfigurationSettings(_configuration);
_configuration.ChangeBasePath(_environment.ContentRootPath);
return this;
}

public IHostBuilder ConfigureServices(Action<HostBuilderContext, IServiceCollection> configureDelegate)
{
// HostingHostBuilderExtensions.ConfigureDefaults calls this via ConfigureLogging
// during the initial config stage. It should be called again later on the ConfigureHostBuilder.
return this;
}

public IHostBuilder UseServiceProviderFactory<TContainerBuilder>(IServiceProviderFactory<TContainerBuilder> factory) where TContainerBuilder : notnull
{
// This is not called by HostingHostBuilderExtensions.ConfigureDefaults currently, but that chould change in the future.
// If this does get called in the future, it should be called again at a later stage on the ConfigureHostBuilder.
return this;
}

public IHostBuilder UseServiceProviderFactory<TContainerBuilder>(Func<HostBuilderContext, IServiceProviderFactory<TContainerBuilder>> factory) where TContainerBuilder : notnull
{
// HostingHostBuilderExtensions.ConfigureDefaults calls this via UseDefaultServiceProvider
// during the initial config stage. It should be called again later on the ConfigureHostBuilder.
return this;
}
}
}
200 changes: 200 additions & 0 deletions src/DefaultBuilder/src/Configuration.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
// 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;
using System.Collections.Generic;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Primitives;

// TODO: Microsft.Extensions.Configuration API Proposal
namespace Microsoft.AspNetCore.Builder
{
/// <summary>
/// Configuration is mutable configuration object. It is both a configuration builder and an IConfigurationRoot.
/// As sources are added, it updates its current view of configuration. Once Build is called, configuration is frozen.
/// </summary>
public sealed class Configuration : IConfigurationRoot, IConfigurationBuilder
{
private readonly ConfigurationBuilder _builder = new();
private IConfigurationRoot _configuration;

/// <summary>
/// Gets or sets a configuration value.
/// </summary>
/// <param name="key">The configuration key.</param>
/// <returns>The configuration value.</returns>
public string this[string key] { get => _configuration[key]; set => _configuration[key] = value; }

/// <summary>
/// Gets a configuration sub-section with the specified key.
/// </summary>
/// <param name="key">The key of the configuration section.</param>
/// <returns>The <see cref="IConfigurationSection"/>.</returns>
/// <remarks>
/// This method will never return <c>null</c>. If no matching sub-section is found with the specified key,
/// an empty <see cref="IConfigurationSection"/> will be returned.
/// </remarks>
public IConfigurationSection GetSection(string key)
{
return _configuration.GetSection(key);
}

/// <summary>
/// Gets the immediate descendant configuration sub-sections.
/// </summary>
/// <returns>The configuration sub-sections.</returns>
public IEnumerable<IConfigurationSection> GetChildren() => _configuration.GetChildren();

IDictionary<string, object> IConfigurationBuilder.Properties => _builder.Properties;

// TODO: Handle modifications to Sources and keep the configuration root in sync
IList<IConfigurationSource> IConfigurationBuilder.Sources => Sources;

internal IList<IConfigurationSource> Sources { get; }

IEnumerable<IConfigurationProvider> IConfigurationRoot.Providers => _configuration.Providers;

/// <summary>
/// Creates a new <see cref="Configuration"/>.
/// </summary>
public Configuration()
{
_configuration = _builder.Build();

var sources = new ConfigurationSources(_builder.Sources, UpdateConfigurationRoot);

Sources = sources;
}

internal void ChangeBasePath(string path)
{
this.SetBasePath(path);
UpdateConfigurationRoot();
}

internal void ChangeFileProvider(IFileProvider fileProvider)
{
this.SetFileProvider(fileProvider);
UpdateConfigurationRoot();
}

private void UpdateConfigurationRoot()
{
var current = _configuration;
if (current is IDisposable disposable)
{
disposable.Dispose();
}
_configuration = _builder.Build();
}

IConfigurationBuilder IConfigurationBuilder.Add(IConfigurationSource source)
{
Sources.Add(source);
return this;
}

IConfigurationRoot IConfigurationBuilder.Build()
{
// No more modification is expected after this final build
UpdateConfigurationRoot();
return this;
}

IChangeToken IConfiguration.GetReloadToken()
{
// REVIEW: Is this correct?
return _configuration.GetReloadToken();
}

void IConfigurationRoot.Reload()
{
_configuration.Reload();
}

// On source modifications, we rebuild configuration
private class ConfigurationSources : IList<IConfigurationSource>
{
private readonly IList<IConfigurationSource> _sources;
private readonly Action _sourcesModified;

public ConfigurationSources(IList<IConfigurationSource> sources, Action sourcesModified)
{
_sources = sources;
_sourcesModified = sourcesModified;
}

public IConfigurationSource this[int index]
{
get => _sources[index];
set
{
_sources[index] = value;
_sourcesModified();
}
}

public int Count => _sources.Count;

public bool IsReadOnly => _sources.IsReadOnly;

public void Add(IConfigurationSource item)
{
_sources.Add(item);
_sourcesModified();
}

public void Clear()
{
_sources.Clear();
_sourcesModified();
}

public bool Contains(IConfigurationSource item)
{
return _sources.Contains(item);
}

public void CopyTo(IConfigurationSource[] array, int arrayIndex)
{
_sources.CopyTo(array, arrayIndex);
}

public IEnumerator<IConfigurationSource> GetEnumerator()
{
return _sources.GetEnumerator();
}

public int IndexOf(IConfigurationSource item)
{
return _sources.IndexOf(item);
}

public void Insert(int index, IConfigurationSource item)
{
_sources.Insert(index, item);
_sourcesModified();
}

public bool Remove(IConfigurationSource item)
{
var removed = _sources.Remove(item);
_sourcesModified();
return removed;
}

public void RemoveAt(int index)
{
_sources.RemoveAt(index);
_sourcesModified();
}

IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
}
}
Loading