();
+ if (tabsList == null)
+ {
+ continue;
+ }
+ foreach (var tab in tabsList)
+ {
+ var hasUrl = tab.TryGetProperty("url", out var urlInTab);
+ var hasActor = tab.TryGetProperty("actor", out var actorInTab);
+ var hasBrowserId = tab.TryGetProperty("browserId", out var browserIdInTab);
+ if (string.IsNullOrEmpty(consoleActorId))
+ {
+ if (hasUrl && urlInTab.GetString()?.StartsWith("about:debugging#", StringComparison.InvariantCultureIgnoreCase) == true)
+ {
+ foundAboutDebugging = true;
+
+ toCmd = hasActor ? actorInTab.GetString() : "";
+ if (tabToRedirect != -1)
+ {
+ break;
+ }
+ }
+ if (hasUrl && urlInTab.GetString()?.Equals(targetApplicationUrl, StringComparison.Ordinal) == true)
+ {
+ tabToRedirect = hasBrowserId ? browserIdInTab.GetInt32() : -1;
+ if (foundAboutDebugging)
+ {
+ break;
+ }
+ }
+ }
+ else if (hasUrl && urlInTab.GetString()?.StartsWith("about:devtools", StringComparison.InvariantCultureIgnoreCase) == true)
+ {
+ return;
+ }
+ }
+ if (!foundAboutDebugging)
+ {
+ context.Response.StatusCode = 404;
+ await context.Response.WriteAsync("WARNING: Open about:debugging tab before pressing Debugging Hotkey");
+ return;
+ }
+ if (string.IsNullOrEmpty(consoleActorId))
+ {
+ await EvaluateOnBrowser(toStream, consoleActorId, $"if (AboutDebugging.store.getState().runtimes.networkRuntimes.find(element => element.id == \"{_debugProxyUrl}\").runtimeDetails !== null) {{ AboutDebugging.actions.selectPage(\"runtime\", \"{_debugProxyUrl}\"); if (AboutDebugging.store.getState().runtimes.selectedRuntimeId == \"{_debugProxyUrl}\") AboutDebugging.actions.inspectDebugTarget(\"tab\", {tabToRedirect})}};", token);
+ }
+ }
+ }
+ if (!string.IsNullOrEmpty(consoleActorId))
+ {
+ var hasInput = res.TryGetProperty("input", out var input);
+ if (hasInput && input.GetString()?.StartsWith("AboutDebugging.actions.addNetworkLocation(", StringComparison.InvariantCultureIgnoreCase) == true)
+ {
+ await EvaluateOnBrowser(toStream, consoleActorId, $"if (AboutDebugging.store.getState().runtimes.networkRuntimes.find(element => element.id == \"{_debugProxyUrl}\").runtimeDetails !== null) {{ AboutDebugging.actions.selectPage(\"runtime\", \"{_debugProxyUrl}\"); if (AboutDebugging.store.getState().runtimes.selectedRuntimeId == \"{_debugProxyUrl}\") AboutDebugging.actions.inspectDebugTarget(\"tab\", {tabToRedirect})}};", token);
+ }
+ if (hasInput && input.GetString()?.StartsWith("if (AboutDebugging.store.getState()", StringComparison.InvariantCultureIgnoreCase) == true)
+ {
+ await EvaluateOnBrowser(toStream, consoleActorId, $"if (AboutDebugging.store.getState().runtimes.networkRuntimes.find(element => element.id == \"{_debugProxyUrl}\").runtimeDetails !== null) {{ AboutDebugging.actions.selectPage(\"runtime\", \"{_debugProxyUrl}\"); if (AboutDebugging.store.getState().runtimes.selectedRuntimeId == \"{_debugProxyUrl}\") AboutDebugging.actions.inspectDebugTarget(\"tab\", {tabToRedirect})}};", token);
+ }
+ }
+ else
+ {
+ var hasTarget = res.TryGetProperty("target", out var target);
+ JsonElement consoleActor = new();
+ var hasConsoleActor = hasTarget && target.TryGetProperty("consoleActor", out consoleActor);
+ var hasActor = res.TryGetProperty("actor", out var actor);
+ if (hasConsoleActor && !string.IsNullOrEmpty(consoleActor.GetString()))
+ {
+ consoleActorId = consoleActor.GetString();
+ await EvaluateOnBrowser(toStream, consoleActorId, $"AboutDebugging.actions.addNetworkLocation(\"{_debugProxyUrl}\"); AboutDebugging.actions.connectRuntime(\"{_debugProxyUrl}\");", token);
+ }
+ else if (hasActor && !string.IsNullOrEmpty(actor.GetString()))
+ {
+ dynamic messageWatchTargets = new ExpandoObject();
+ messageWatchTargets.type = "watchTargets";
+ messageWatchTargets.targetType = "frame";
+ messageWatchTargets.to = actor.GetString();
+ await SendMessageToBrowser(toStream, messageWatchTargets, token);
+ dynamic messageWatchResources = new ExpandoObject();
+ messageWatchResources.type = "watchResources";
+ messageWatchResources.resourceTypes = new string[1] { "console-message" };
+ messageWatchResources.to = actor.GetString();
+ await SendMessageToBrowser(toStream, messageWatchResources, token);
+ }
+ else if (!string.IsNullOrEmpty(toCmd))
+ {
+ dynamic messageGetWatcher = new ExpandoObject();
+ messageGetWatcher.type = "getWatcher";
+ messageGetWatcher.isServerTargetSwitchingEnabled = true;
+ messageGetWatcher.to = toCmd;
+ await SendMessageToBrowser(toStream, messageGetWatcher, token);
+ }
+ }
+ }
+
+ }
+ return;
+ }
+
///
/// Display the ui.
///
diff --git a/src/Components/WebAssembly/Server/src/WebAssemblyNetDebugProxyAppBuilderExtensions.cs b/src/Components/WebAssembly/Server/src/WebAssemblyNetDebugProxyAppBuilderExtensions.cs
index 2d83f89682a4..59b81f59eee8 100644
--- a/src/Components/WebAssembly/Server/src/WebAssemblyNetDebugProxyAppBuilderExtensions.cs
+++ b/src/Components/WebAssembly/Server/src/WebAssemblyNetDebugProxyAppBuilderExtensions.cs
@@ -31,8 +31,12 @@ public static void UseWebAssemblyDebugging(this IApplicationBuilder app)
browserUrl = new Uri(browserParam);
devToolsHost = $"http://{browserUrl.Host}:{browserUrl.Port}";
}
-
- var debugProxyBaseUrl = await DebugProxyLauncher.EnsureLaunchedAndGetUrl(context.RequestServices, devToolsHost);
+ var isFirefox = string.IsNullOrEmpty(queryParams.Get("isFirefox")) ? false : true;
+ if (isFirefox)
+ {
+ devToolsHost = "localhost:6000";
+ }
+ var debugProxyBaseUrl = await DebugProxyLauncher.EnsureLaunchedAndGetUrl(context.RequestServices, devToolsHost, isFirefox);
var requestPath = context.Request.Path.ToString();
if (requestPath == string.Empty)
{
@@ -43,7 +47,14 @@ public static void UseWebAssemblyDebugging(this IApplicationBuilder app)
{
case "/":
var targetPickerUi = new TargetPickerUi(debugProxyBaseUrl, devToolsHost);
- await targetPickerUi.Display(context);
+ if (isFirefox)
+ {
+ await targetPickerUi.DisplayFirefox(context);
+ }
+ else
+ {
+ await targetPickerUi.Display(context);
+ }
break;
case "/ws-proxy":
context.Response.Redirect($"{debugProxyBaseUrl}{browserUrl!.PathAndQuery}");
diff --git a/src/Components/test/E2ETest/Tests/ErrorBoundaryTest.cs b/src/Components/test/E2ETest/Tests/ErrorBoundaryTest.cs
index ba62819484cd..9ff97acdb14c 100644
--- a/src/Components/test/E2ETest/Tests/ErrorBoundaryTest.cs
+++ b/src/Components/test/E2ETest/Tests/ErrorBoundaryTest.cs
@@ -35,6 +35,8 @@ protected override void InitializeAsyncCore()
[InlineData("afterrender-sync")]
[InlineData("afterrender-async")]
[InlineData("while-rendering")]
+ [InlineData("dispatch-sync-exception")]
+ [InlineData("dispatch-async-exception")]
public void CanHandleExceptions(string triggerId)
{
var container = Browser.Exists(By.Id("error-boundary-container"));
diff --git a/src/Components/test/testassets/BasicTestApp/ErrorBoundaryTest/ErrorBoundaryCases.razor b/src/Components/test/testassets/BasicTestApp/ErrorBoundaryTest/ErrorBoundaryCases.razor
index 3b87c1e7c54f..bcc4aec249e0 100644
--- a/src/Components/test/testassets/BasicTestApp/ErrorBoundaryTest/ErrorBoundaryCases.razor
+++ b/src/Components/test/testassets/BasicTestApp/ErrorBoundaryTest/ErrorBoundaryCases.razor
@@ -99,6 +99,14 @@
+
+Dispatch exception to renderer
+Use DispatchExceptionAsync to see if exceptions are correctly dispatched to the renderer.
+
+
+
+
+
@code {
private bool throwInOnParametersSet;
private bool throwInOnParametersSetAsync;
@@ -143,4 +151,15 @@
// Before it completes, dispose its enclosing error boundary
disposalTestRemoveErrorBoundary = true;
}
+
+ async Task SyncExceptionDispatch()
+ {
+ await DispatchExceptionAsync(new InvalidTimeZoneException("Synchronous exception in SyncExceptionDispatch"));
+ }
+
+ async Task AsyncExceptionDispatch()
+ {
+ await Task.Yield();
+ await DispatchExceptionAsync(new InvalidTimeZoneException("Asynchronous exception in AsyncExceptionDispatch"));
+ }
}
diff --git a/src/DefaultBuilder/src/PublicAPI.Unshipped.txt b/src/DefaultBuilder/src/PublicAPI.Unshipped.txt
index 7dc5c58110bf..c4dde0ab6378 100644
--- a/src/DefaultBuilder/src/PublicAPI.Unshipped.txt
+++ b/src/DefaultBuilder/src/PublicAPI.Unshipped.txt
@@ -1 +1,4 @@
#nullable enable
+static Microsoft.AspNetCore.Builder.WebApplication.CreateSlimBuilder() -> Microsoft.AspNetCore.Builder.WebApplicationBuilder!
+static Microsoft.AspNetCore.Builder.WebApplication.CreateSlimBuilder(Microsoft.AspNetCore.Builder.WebApplicationOptions! options) -> Microsoft.AspNetCore.Builder.WebApplicationBuilder!
+static Microsoft.AspNetCore.Builder.WebApplication.CreateSlimBuilder(string![]! args) -> Microsoft.AspNetCore.Builder.WebApplicationBuilder!
diff --git a/src/DefaultBuilder/src/WebApplication.cs b/src/DefaultBuilder/src/WebApplication.cs
index 1a9db8c6df1e..6056b3ff4d02 100644
--- a/src/DefaultBuilder/src/WebApplication.cs
+++ b/src/DefaultBuilder/src/WebApplication.cs
@@ -98,6 +98,13 @@ public static WebApplication Create(string[]? args = null) =>
public static WebApplicationBuilder CreateBuilder() =>
new(new());
+ ///
+ /// Initializes a new instance of the class with minimal defaults.
+ ///
+ /// The .
+ public static WebApplicationBuilder CreateSlimBuilder() =>
+ new(new(), slim: true);
+
///
/// Initializes a new instance of the class with preconfigured defaults.
///
@@ -106,6 +113,14 @@ public static WebApplicationBuilder CreateBuilder() =>
public static WebApplicationBuilder CreateBuilder(string[] args) =>
new(new() { Args = args });
+ ///
+ /// Initializes a new instance of the class with minimal defaults.
+ ///
+ /// Command line arguments
+ /// The .
+ public static WebApplicationBuilder CreateSlimBuilder(string[] args) =>
+ new(new() { Args = args }, slim: true);
+
///
/// Initializes a new instance of the class with preconfigured defaults.
///
@@ -114,6 +129,14 @@ public static WebApplicationBuilder CreateBuilder(string[] args) =>
public static WebApplicationBuilder CreateBuilder(WebApplicationOptions options) =>
new(options);
+ ///
+ /// Initializes a new instance of the class with minimal defaults.
+ ///
+ /// The to configure the .
+ /// The .
+ public static WebApplicationBuilder CreateSlimBuilder(WebApplicationOptions options) =>
+ new(options, slim: true);
+
///
/// Start the application.
///
diff --git a/src/DefaultBuilder/src/WebApplicationBuilder.cs b/src/DefaultBuilder/src/WebApplicationBuilder.cs
index 3da1ac4e83a7..0f7bca59694c 100644
--- a/src/DefaultBuilder/src/WebApplicationBuilder.cs
+++ b/src/DefaultBuilder/src/WebApplicationBuilder.cs
@@ -86,6 +86,97 @@ internal WebApplicationBuilder(WebApplicationOptions options, Action? configureDefaults = null)
+ {
+ Debug.Assert(slim, "should only be called with slim: true");
+
+ var configuration = new ConfigurationManager();
+
+ configuration.AddEnvironmentVariables(prefix: "ASPNETCORE_");
+
+ // add the default host configuration sources, so they are picked up by the HostApplicationBuilder constructor.
+ // These won't be added by HostApplicationBuilder since DisableDefaults = true.
+ configuration.AddEnvironmentVariables(prefix: "DOTNET_");
+ if (options.Args is { Length: > 0 } args)
+ {
+ configuration.AddCommandLine(args);
+ }
+
+ _hostApplicationBuilder = new HostApplicationBuilder(new HostApplicationBuilderSettings
+ {
+ DisableDefaults = true,
+ Args = options.Args,
+ ApplicationName = options.ApplicationName,
+ EnvironmentName = options.EnvironmentName,
+ ContentRootPath = options.ContentRootPath,
+ Configuration = configuration,
+ });
+
+ // configure the ServiceProviderOptions here since DisableDefaults = true means HostApplicationBuilder won't.
+ var serviceProviderFactory = GetServiceProviderFactory(_hostApplicationBuilder);
+ _hostApplicationBuilder.ConfigureContainer(serviceProviderFactory);
+
+ // Set WebRootPath if necessary
+ if (options.WebRootPath is not null)
+ {
+ Configuration.AddInMemoryCollection(new[]
+ {
+ new KeyValuePair(WebHostDefaults.WebRootKey, options.WebRootPath),
+ });
+ }
+
+ // Run methods to configure web host defaults early to populate services
+ var bootstrapHostBuilder = new BootstrapHostBuilder(_hostApplicationBuilder);
+
+ // This is for testing purposes
+ configureDefaults?.Invoke(bootstrapHostBuilder);
+
+ bootstrapHostBuilder.ConfigureSlimWebHost(
+ webHostBuilder =>
+ {
+ AspNetCore.WebHost.UseKestrel(webHostBuilder);
+
+ webHostBuilder.Configure(ConfigureEmptyApplication);
+
+ webHostBuilder.UseSetting(WebHostDefaults.ApplicationKey, _hostApplicationBuilder.Environment.ApplicationName ?? "");
+ webHostBuilder.UseSetting(WebHostDefaults.PreventHostingStartupKey, Configuration[WebHostDefaults.PreventHostingStartupKey]);
+ webHostBuilder.UseSetting(WebHostDefaults.HostingStartupAssembliesKey, Configuration[WebHostDefaults.HostingStartupAssembliesKey]);
+ webHostBuilder.UseSetting(WebHostDefaults.HostingStartupExcludeAssembliesKey, Configuration[WebHostDefaults.HostingStartupExcludeAssembliesKey]);
+ },
+ options =>
+ {
+ // We've already applied "ASPNETCORE_" environment variables to hosting config
+ options.SuppressEnvironmentConfiguration = true;
+ });
+
+ // This applies the config from ConfigureWebHostDefaults
+ // Grab the GenericWebHostService ServiceDescriptor so we can append it after any user-added IHostedServices during Build();
+ _genericWebHostServiceDescriptor = bootstrapHostBuilder.RunDefaultCallbacks();
+
+ // Grab the WebHostBuilderContext from the property bag to use in the ConfigureWebHostBuilder. Then
+ // grab the IWebHostEnvironment from the webHostContext. This also matches the instance in the IServiceCollection.
+ var webHostContext = (WebHostBuilderContext)bootstrapHostBuilder.Properties[typeof(WebHostBuilderContext)];
+ Environment = webHostContext.HostingEnvironment;
+
+ Host = new ConfigureHostBuilder(bootstrapHostBuilder.Context, Configuration, Services);
+ WebHost = new ConfigureWebHostBuilder(webHostContext, Configuration, Services);
+ }
+
+ private static DefaultServiceProviderFactory GetServiceProviderFactory(HostApplicationBuilder hostApplicationBuilder)
+ {
+ if (hostApplicationBuilder.Environment.IsDevelopment())
+ {
+ return new DefaultServiceProviderFactory(
+ new ServiceProviderOptions
+ {
+ ValidateScopes = true,
+ ValidateOnBuild = true,
+ });
+ }
+
+ return new DefaultServiceProviderFactory();
+ }
+
///
/// Provides information about the web hosting environment an application is running.
///
@@ -133,6 +224,46 @@ public WebApplication Build()
}
private void ConfigureApplication(WebHostBuilderContext context, IApplicationBuilder app)
+ {
+ ConfigureApplicationCore(
+ context,
+ app,
+ processAuthMiddlewares: () =>
+ {
+ Debug.Assert(_builtApplication is not null);
+
+ // Process authorization and authentication middlewares independently to avoid
+ // registering middlewares for services that do not exist
+ var serviceProviderIsService = _builtApplication.Services.GetService();
+ if (serviceProviderIsService?.IsService(typeof(IAuthenticationSchemeProvider)) is true)
+ {
+ // Don't add more than one instance of the middleware
+ if (!_builtApplication.Properties.ContainsKey(AuthenticationMiddlewareSetKey))
+ {
+ // The Use invocations will set the property on the outer pipeline,
+ // but we want to set it on the inner pipeline as well.
+ _builtApplication.Properties[AuthenticationMiddlewareSetKey] = true;
+ app.UseAuthentication();
+ }
+ }
+
+ if (serviceProviderIsService?.IsService(typeof(IAuthorizationHandlerProvider)) is true)
+ {
+ if (!_builtApplication.Properties.ContainsKey(AuthorizationMiddlewareSetKey))
+ {
+ _builtApplication.Properties[AuthorizationMiddlewareSetKey] = true;
+ app.UseAuthorization();
+ }
+ }
+ });
+ }
+
+ private void ConfigureEmptyApplication(WebHostBuilderContext context, IApplicationBuilder app)
+ {
+ ConfigureApplicationCore(context, app, processAuthMiddlewares: null);
+ }
+
+ private void ConfigureApplicationCore(WebHostBuilderContext context, IApplicationBuilder app, Action? processAuthMiddlewares)
{
Debug.Assert(_builtApplication is not null);
@@ -173,29 +304,7 @@ private void ConfigureApplication(WebHostBuilderContext context, IApplicationBui
}
}
- // Process authorization and authentication middlewares independently to avoid
- // registering middlewares for services that do not exist
- var serviceProviderIsService = _builtApplication.Services.GetService();
- if (serviceProviderIsService?.IsService(typeof(IAuthenticationSchemeProvider)) is true)
- {
- // Don't add more than one instance of the middleware
- if (!_builtApplication.Properties.ContainsKey(AuthenticationMiddlewareSetKey))
- {
- // The Use invocations will set the property on the outer pipeline,
- // but we want to set it on the inner pipeline as well.
- _builtApplication.Properties[AuthenticationMiddlewareSetKey] = true;
- app.UseAuthentication();
- }
- }
-
- if (serviceProviderIsService?.IsService(typeof(IAuthorizationHandlerProvider)) is true)
- {
- if (!_builtApplication.Properties.ContainsKey(AuthorizationMiddlewareSetKey))
- {
- _builtApplication.Properties[AuthorizationMiddlewareSetKey] = true;
- app.UseAuthorization();
- }
- }
+ processAuthMiddlewares?.Invoke();
// Wire the source pipeline to run in the destination pipeline
app.Use(next =>
diff --git a/src/DefaultBuilder/src/WebHost.cs b/src/DefaultBuilder/src/WebHost.cs
index 156efd0e326e..4a49c3ef8e3c 100644
--- a/src/DefaultBuilder/src/WebHost.cs
+++ b/src/DefaultBuilder/src/WebHost.cs
@@ -222,6 +222,16 @@ internal static void ConfigureWebDefaults(IWebHostBuilder builder)
StaticWebAssetsLoader.UseStaticWebAssets(ctx.HostingEnvironment, ctx.Configuration);
}
});
+
+ UseKestrel(builder);
+
+ builder
+ .UseIIS()
+ .UseIISIntegration();
+ }
+
+ internal static void UseKestrel(IWebHostBuilder builder)
+ {
builder.UseKestrel((builderContext, options) =>
{
options.Configure(builderContext.Configuration.GetSection("Kestrel"), reloadOnChange: true);
@@ -248,9 +258,7 @@ internal static void ConfigureWebDefaults(IWebHostBuilder builder)
services.AddTransient, ForwardedHeadersOptionsSetup>();
services.AddRouting();
- })
- .UseIIS()
- .UseIISIntegration();
+ });
}
///
diff --git a/src/DefaultBuilder/test/Microsoft.AspNetCore.Tests/WebApplicationTests.cs b/src/DefaultBuilder/test/Microsoft.AspNetCore.Tests/WebApplicationTests.cs
index ea386a85e0b2..104e13788f2c 100644
--- a/src/DefaultBuilder/test/Microsoft.AspNetCore.Tests/WebApplicationTests.cs
+++ b/src/DefaultBuilder/test/Microsoft.AspNetCore.Tests/WebApplicationTests.cs
@@ -36,20 +36,77 @@ namespace Microsoft.AspNetCore.Tests;
public class WebApplicationTests
{
- [Fact]
- public async Task WebApplicationBuilder_New()
+ public delegate WebApplicationBuilder CreateBuilderFunc();
+ public delegate WebApplicationBuilder CreateBuilderArgsFunc(string[] args);
+ public delegate WebApplicationBuilder CreateBuilderOptionsFunc(WebApplicationOptions options);
+ public delegate WebApplicationBuilder WebApplicationBuilderConstructorFunc(WebApplicationOptions options, Action configureDefaults);
+
+ private static WebApplicationBuilder CreateBuilder() => WebApplication.CreateBuilder();
+ private static WebApplicationBuilder CreateSlimBuilder() => WebApplication.CreateSlimBuilder();
+
+ public static IEnumerable