Skip to content

Commit bad8559

Browse files
authored
Add WebApplication.CreateEmptyBuilder (#49246)
* Add WebApplication.CreateEmptyBuilder Allow an empty WebApplicationBuilder to be created without default behavior. There is no default configuration sources (ex. environment variables or appsettings.json files), no logging, and no web server (ex. Kestrel) configured by default. These can all be added explicitly by the app. The following middleware can be enabled by the app: - Routing and Endpoints, if the app calls Services.AddRouting, and then MapXXX or registers an EndpointDataSource manually - AuthN/Z, if the app adds the corresponding Auth services Fix #48811 * Remove Routing from the "Empty" builder. It is up to the app to configure what they want. This is the Empty WebApplication. * Respect hosting startup configuration. Refactor to share logic.
1 parent bec0339 commit bad8559

File tree

5 files changed

+243
-37
lines changed

5 files changed

+243
-37
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#nullable enable
2+
static Microsoft.AspNetCore.Builder.WebApplication.CreateEmptyBuilder(Microsoft.AspNetCore.Builder.WebApplicationOptions! options) -> Microsoft.AspNetCore.Builder.WebApplicationBuilder!
23
static Microsoft.AspNetCore.Builder.WebApplication.CreateSlimBuilder() -> Microsoft.AspNetCore.Builder.WebApplicationBuilder!
34
static Microsoft.AspNetCore.Builder.WebApplication.CreateSlimBuilder(Microsoft.AspNetCore.Builder.WebApplicationOptions! options) -> Microsoft.AspNetCore.Builder.WebApplicationBuilder!
45
static Microsoft.AspNetCore.Builder.WebApplication.CreateSlimBuilder(string![]! args) -> Microsoft.AspNetCore.Builder.WebApplicationBuilder!

src/DefaultBuilder/src/WebApplication.cs

+8
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,14 @@ public static WebApplicationBuilder CreateBuilder(WebApplicationOptions options)
141141
public static WebApplicationBuilder CreateSlimBuilder(WebApplicationOptions options) =>
142142
new(options, slim: true);
143143

144+
/// <summary>
145+
/// Initializes a new instance of the <see cref="WebApplicationBuilder"/> class with no defaults.
146+
/// </summary>
147+
/// <param name="options">The <see cref="WebApplicationOptions"/> to configure the <see cref="WebApplicationBuilder"/>.</param>
148+
/// <returns>The <see cref="WebApplicationBuilder"/>.</returns>
149+
public static WebApplicationBuilder CreateEmptyBuilder(WebApplicationOptions options) =>
150+
new(options, slim: false, empty: true);
151+
144152
/// <summary>
145153
/// Start the application.
146154
/// </summary>

src/DefaultBuilder/src/WebApplicationBuilder.cs

+85-26
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using System.Diagnostics;
5+
using System.Diagnostics.CodeAnalysis;
56
using System.Reflection;
67
using Microsoft.AspNetCore.Authentication;
78
using Microsoft.AspNetCore.Authorization;
@@ -64,28 +65,15 @@ internal WebApplicationBuilder(WebApplicationOptions options, Action<IHostBuilde
6465
// Runs inline.
6566
webHostBuilder.Configure(ConfigureApplication);
6667

67-
webHostBuilder.UseSetting(WebHostDefaults.ApplicationKey, _hostApplicationBuilder.Environment.ApplicationName ?? "");
68-
webHostBuilder.UseSetting(WebHostDefaults.PreventHostingStartupKey, Configuration[WebHostDefaults.PreventHostingStartupKey]);
69-
webHostBuilder.UseSetting(WebHostDefaults.HostingStartupAssembliesKey, Configuration[WebHostDefaults.HostingStartupAssembliesKey]);
70-
webHostBuilder.UseSetting(WebHostDefaults.HostingStartupExcludeAssembliesKey, Configuration[WebHostDefaults.HostingStartupExcludeAssembliesKey]);
68+
InitializeWebHostSettings(webHostBuilder);
7169
},
7270
options =>
7371
{
7472
// We've already applied "ASPNETCORE_" environment variables to hosting config
7573
options.SuppressEnvironmentConfiguration = true;
7674
});
7775

78-
// This applies the config from ConfigureWebHostDefaults
79-
// Grab the GenericWebHostService ServiceDescriptor so we can append it after any user-added IHostedServices during Build();
80-
_genericWebHostServiceDescriptor = bootstrapHostBuilder.RunDefaultCallbacks();
81-
82-
// Grab the WebHostBuilderContext from the property bag to use in the ConfigureWebHostBuilder. Then
83-
// grab the IWebHostEnvironment from the webHostContext. This also matches the instance in the IServiceCollection.
84-
var webHostContext = (WebHostBuilderContext)bootstrapHostBuilder.Properties[typeof(WebHostBuilderContext)];
85-
Environment = webHostContext.HostingEnvironment;
86-
87-
Host = new ConfigureHostBuilder(bootstrapHostBuilder.Context, Configuration, Services);
88-
WebHost = new ConfigureWebHostBuilder(webHostContext, Configuration, Services);
76+
_genericWebHostServiceDescriptor = InitializeHosting(bootstrapHostBuilder);
8977
}
9078

9179
internal WebApplicationBuilder(WebApplicationOptions options, bool slim, Action<IHostBuilder>? configureDefaults = null)
@@ -138,25 +126,83 @@ internal WebApplicationBuilder(WebApplicationOptions options, bool slim, Action<
138126
bootstrapHostBuilder.ConfigureSlimWebHost(
139127
webHostBuilder =>
140128
{
141-
AspNetCore.WebHost.ConfigureWebDefaultsCore(webHostBuilder);
129+
AspNetCore.WebHost.ConfigureWebDefaultsSlim(webHostBuilder);
142130

143131
// Runs inline.
144132
webHostBuilder.Configure(ConfigureApplication);
145133

146-
webHostBuilder.UseSetting(WebHostDefaults.ApplicationKey, _hostApplicationBuilder.Environment.ApplicationName ?? "");
147-
webHostBuilder.UseSetting(WebHostDefaults.PreventHostingStartupKey, Configuration[WebHostDefaults.PreventHostingStartupKey]);
148-
webHostBuilder.UseSetting(WebHostDefaults.HostingStartupAssembliesKey, Configuration[WebHostDefaults.HostingStartupAssembliesKey]);
149-
webHostBuilder.UseSetting(WebHostDefaults.HostingStartupExcludeAssembliesKey, Configuration[WebHostDefaults.HostingStartupExcludeAssembliesKey]);
134+
InitializeWebHostSettings(webHostBuilder);
150135
},
151136
options =>
152137
{
153138
// We've already applied "ASPNETCORE_" environment variables to hosting config
154139
options.SuppressEnvironmentConfiguration = true;
155140
});
156141

142+
_genericWebHostServiceDescriptor = InitializeHosting(bootstrapHostBuilder);
143+
}
144+
145+
internal WebApplicationBuilder(WebApplicationOptions options, bool slim, bool empty, Action<IHostBuilder>? configureDefaults = null)
146+
{
147+
Debug.Assert(!slim, "should only be called with slim: false");
148+
Debug.Assert(empty, "should only be called with empty: true");
149+
150+
var configuration = new ConfigurationManager();
151+
152+
// empty builder should still default the ContentRoot as usual. This is the expected behavior for all WebApplicationBuilders.
153+
SetDefaultContentRoot(options, configuration);
154+
155+
_hostApplicationBuilder = Microsoft.Extensions.Hosting.Host.CreateEmptyApplicationBuilder(new HostApplicationBuilderSettings
156+
{
157+
Args = options.Args,
158+
ApplicationName = options.ApplicationName,
159+
EnvironmentName = options.EnvironmentName,
160+
ContentRootPath = options.ContentRootPath,
161+
Configuration = configuration,
162+
});
163+
164+
// Set WebRootPath if necessary
165+
if (options.WebRootPath is not null)
166+
{
167+
Configuration.AddInMemoryCollection(new[]
168+
{
169+
new KeyValuePair<string, string?>(WebHostDefaults.WebRootKey, options.WebRootPath),
170+
});
171+
}
172+
173+
// Run methods to configure web host defaults early to populate services
174+
var bootstrapHostBuilder = new BootstrapHostBuilder(_hostApplicationBuilder);
175+
176+
// This is for testing purposes
177+
configureDefaults?.Invoke(bootstrapHostBuilder);
178+
179+
bootstrapHostBuilder.ConfigureSlimWebHost(
180+
webHostBuilder =>
181+
{
182+
// Note this doesn't configure any WebHost server - Kestrel or otherwise.
183+
// It also doesn't register Routing, HostFiltering, or ForwardedHeaders.
184+
// It is "empty" and up to the caller to configure these services.
185+
186+
// Runs inline.
187+
webHostBuilder.Configure((context, app) => ConfigureApplication(context, app, allowDeveloperExceptionPage: false));
188+
189+
InitializeWebHostSettings(webHostBuilder);
190+
},
191+
options =>
192+
{
193+
// This is an "empty" builder, so don't add the "ASPNETCORE_" environment variables
194+
options.SuppressEnvironmentConfiguration = true;
195+
});
196+
197+
_genericWebHostServiceDescriptor = InitializeHosting(bootstrapHostBuilder);
198+
}
199+
200+
[MemberNotNull(nameof(Environment), nameof(Host), nameof(WebHost))]
201+
private ServiceDescriptor InitializeHosting(BootstrapHostBuilder bootstrapHostBuilder)
202+
{
157203
// This applies the config from ConfigureWebHostDefaults
158204
// Grab the GenericWebHostService ServiceDescriptor so we can append it after any user-added IHostedServices during Build();
159-
_genericWebHostServiceDescriptor = bootstrapHostBuilder.RunDefaultCallbacks();
205+
var genericWebHostServiceDescriptor = bootstrapHostBuilder.RunDefaultCallbacks();
160206

161207
// Grab the WebHostBuilderContext from the property bag to use in the ConfigureWebHostBuilder. Then
162208
// grab the IWebHostEnvironment from the webHostContext. This also matches the instance in the IServiceCollection.
@@ -165,6 +211,16 @@ internal WebApplicationBuilder(WebApplicationOptions options, bool slim, Action<
165211

166212
Host = new ConfigureHostBuilder(bootstrapHostBuilder.Context, Configuration, Services);
167213
WebHost = new ConfigureWebHostBuilder(webHostContext, Configuration, Services);
214+
215+
return genericWebHostServiceDescriptor;
216+
}
217+
218+
private void InitializeWebHostSettings(IWebHostBuilder webHostBuilder)
219+
{
220+
webHostBuilder.UseSetting(WebHostDefaults.ApplicationKey, _hostApplicationBuilder.Environment.ApplicationName ?? "");
221+
webHostBuilder.UseSetting(WebHostDefaults.PreventHostingStartupKey, Configuration[WebHostDefaults.PreventHostingStartupKey]);
222+
webHostBuilder.UseSetting(WebHostDefaults.HostingStartupAssembliesKey, Configuration[WebHostDefaults.HostingStartupAssembliesKey]);
223+
webHostBuilder.UseSetting(WebHostDefaults.HostingStartupExcludeAssembliesKey, Configuration[WebHostDefaults.HostingStartupExcludeAssembliesKey]);
168224
}
169225

170226
private static DefaultServiceProviderFactory GetServiceProviderFactory(HostApplicationBuilder hostApplicationBuilder)
@@ -272,7 +328,7 @@ private static void AddDefaultServicesSlim(ConfigurationManager configuration, I
272328
/// <summary>
273329
/// Provides information about the web hosting environment an application is running.
274330
/// </summary>
275-
public IWebHostEnvironment Environment { get; }
331+
public IWebHostEnvironment Environment { get; private set; }
276332

277333
/// <summary>
278334
/// A collection of services for the application to compose. This is useful for adding user provided or framework provided services.
@@ -293,13 +349,13 @@ private static void AddDefaultServicesSlim(ConfigurationManager configuration, I
293349
/// An <see cref="IWebHostBuilder"/> for configuring server specific properties, but not building.
294350
/// To build after configuration, call <see cref="Build"/>.
295351
/// </summary>
296-
public ConfigureWebHostBuilder WebHost { get; }
352+
public ConfigureWebHostBuilder WebHost { get; private set; }
297353

298354
/// <summary>
299355
/// An <see cref="IHostBuilder"/> for configuring host specific properties, but not building.
300356
/// To build after configuration, call <see cref="Build"/>.
301357
/// </summary>
302-
public ConfigureHostBuilder Host { get; }
358+
public ConfigureHostBuilder Host { get; private set; }
303359

304360
IDictionary<object, object> IHostApplicationBuilder.Properties => ((IHostApplicationBuilder)_hostApplicationBuilder).Properties;
305361

@@ -321,7 +377,10 @@ public WebApplication Build()
321377
return _builtApplication;
322378
}
323379

324-
private void ConfigureApplication(WebHostBuilderContext context, IApplicationBuilder app)
380+
private void ConfigureApplication(WebHostBuilderContext context, IApplicationBuilder app) =>
381+
ConfigureApplication(context, app, allowDeveloperExceptionPage: true);
382+
383+
private void ConfigureApplication(WebHostBuilderContext context, IApplicationBuilder app, bool allowDeveloperExceptionPage)
325384
{
326385
Debug.Assert(_builtApplication is not null);
327386

@@ -332,7 +391,7 @@ private void ConfigureApplication(WebHostBuilderContext context, IApplicationBui
332391
app.Properties.Remove(EndpointRouteBuilderKey);
333392
}
334393

335-
if (context.HostingEnvironment.IsDevelopment())
394+
if (allowDeveloperExceptionPage && context.HostingEnvironment.IsDevelopment())
336395
{
337396
app.UseDeveloperExceptionPage();
338397
}

src/DefaultBuilder/src/WebHost.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,7 @@ internal static void ConfigureWebDefaults(IWebHostBuilder builder)
236236
.UseIISIntegration();
237237
}
238238

239-
internal static void ConfigureWebDefaultsCore(IWebHostBuilder builder)
239+
internal static void ConfigureWebDefaultsSlim(IWebHostBuilder builder)
240240
{
241241
ConfigureWebDefaultsWorker(builder.UseKestrelCore().ConfigureKestrel(ConfigureKestrel), configureRouting: null);
242242
}

0 commit comments

Comments
 (0)