From 67e80f598dd18b3ab8a3b10f2161296d5aaf9ea8 Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Mon, 11 Aug 2025 07:16:09 -0400 Subject: [PATCH 01/16] Rename LinkPreload to ResourcePreloader --- aspnetcore/blazor/host-and-deploy/server/index.md | 6 +++--- aspnetcore/release-notes/aspnetcore-10/includes/blazor.md | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/aspnetcore/blazor/host-and-deploy/server/index.md b/aspnetcore/blazor/host-and-deploy/server/index.md index 70135ea17c6b..91b1851d0596 100644 --- a/aspnetcore/blazor/host-and-deploy/server/index.md +++ b/aspnetcore/blazor/host-and-deploy/server/index.md @@ -44,16 +44,16 @@ Each circuit uses approximately 250 KB of memory for a minimal *Hello World*-sty ## Blazor WebAssembly static asset preloading -The `LinkPreload` component in the `App` component's head content (`App.razor`) is used to reference Blazor static assets. The component is placed after the base URL tag (``): +The `ResourcePreloader` component in the `App` component's head content (`App.razor`) is used to reference Blazor static assets. The component is placed after the base URL tag (``): ```razor - + ``` A Razor component is used instead of `` elements because: * The component permits the base URL (`` tag's `href` attribute value) to correctly identify the root of the Blazor app within an ASP.NET Core app. -* The feature can be removed by removing the `LinkPreload` component tag from the `App` component. This is helpful in cases where the app is using a [`loadBootResource` callback](xref:blazor/fundamentals/startup#load-client-side-boot-resources) to modify URLs. +* The feature can be removed by removing the `ResourcePreloader` component tag from the `App` component. This is helpful in cases where the app is using a [`loadBootResource` callback](xref:blazor/fundamentals/startup#load-client-side-boot-resources) to modify URLs. :::moniker-end diff --git a/aspnetcore/release-notes/aspnetcore-10/includes/blazor.md b/aspnetcore/release-notes/aspnetcore-10/includes/blazor.md index 9858b1617a1c..b010c485d377 100644 --- a/aspnetcore/release-notes/aspnetcore-10/includes/blazor.md +++ b/aspnetcore/release-notes/aspnetcore-10/includes/blazor.md @@ -450,17 +450,17 @@ For more information, see ` headers with a `LinkPreload` component (``) for preloading WebAssembly assets in Blazor Web Apps. This permits the app base path configuration (``) to correctly identify the app's root. +We replaced `` headers with a `ResourcePreloader` component (``) for preloading WebAssembly assets in Blazor Web Apps. This permits the app base path configuration (``) to correctly identify the app's root. Removing the component disables the feature if the app is using a [`loadBootResource` callback](xref:blazor/fundamentals/startup?view=aspnetcore-10.0#load-client-side-boot-resources) to modify URLs. -The Blazor Web App template adopts the feature by default in .NET 10, and apps upgrading to .NET 10 can implement the feature by placing the `LinkPreload` component after the base URL tag (``) in the `App` component's head content (`App.razor`): +The Blazor Web App template adopts the feature by default in .NET 10, and apps upgrading to .NET 10 can implement the feature by placing the `ResourcePreloader` component after the base URL tag (``) in the `App` component's head content (`App.razor`): ```diff ... -+ ++ ... ``` From 4ed3a92849b5fb175235fd4295bb6c32a49fe6d1 Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Mon, 11 Aug 2025 07:25:41 -0400 Subject: [PATCH 02/16] Updated API names for Blazor state persistence --- aspnetcore/blazor/components/integration.md | 6 ++--- aspnetcore/blazor/components/lifecycle.md | 2 +- aspnetcore/blazor/security/index.md | 2 +- .../prerendered-state-persistence.md | 20 ++++++++-------- aspnetcore/blazor/state-management/server.md | 24 ++++++++----------- .../aspnetcore-10/includes/blazor.md | 4 ++-- 6 files changed, 27 insertions(+), 31 deletions(-) diff --git a/aspnetcore/blazor/components/integration.md b/aspnetcore/blazor/components/integration.md index 72c0a53eb978..e7b46bca8214 100644 --- a/aspnetcore/blazor/components/integration.md +++ b/aspnetcore/blazor/components/integration.md @@ -408,13 +408,13 @@ In `Pages/_Host.cshtml` of Blazor apps that are `ServerPrerendered` in a Blazor -Decide what state to persist using the service. The `[SupplyParameterFromPersistentComponentState]` attribute applied to a property registers a callback to persist the state during prerendering and loads it when the component renders interactively or the service is instantiated. +Decide what state to persist using the service. The `[PersistentState]` attribute applied to a property registers a callback to persist the state during prerendering and loads it when the component renders interactively or the service is instantiated. In the following example, the `{TYPE}` placeholder represents the type of data to persist (for example, `WeatherForecast[]`). ```razor @code { - [SupplyParameterFromPersistentComponentState] + [PersistentState] public {TYPE} Data { get; set; } protected override async Task OnInitializedAsync() @@ -469,7 +469,7 @@ else } @code { - [SupplyParameterFromPersistentComponentState] + [PersistentState] public WeatherForecast[]? Forecasts { get; set; } protected override async Task OnInitializedAsync() diff --git a/aspnetcore/blazor/components/lifecycle.md b/aspnetcore/blazor/components/lifecycle.md index d88af2350852..6c49c45fa941 100644 --- a/aspnetcore/blazor/components/lifecycle.md +++ b/aspnetcore/blazor/components/lifecycle.md @@ -636,7 +636,7 @@ else } @code { - [SupplyParameterFromPersistentComponentState] + [PersistentState] public string? Data { get; set; } protected override async Task OnInitializedAsync() diff --git a/aspnetcore/blazor/security/index.md b/aspnetcore/blazor/security/index.md index 6db6e394b680..e60d283f4cff 100644 --- a/aspnetcore/blazor/security/index.md +++ b/aspnetcore/blazor/security/index.md @@ -676,7 +676,7 @@ else } @code { - [SupplyParameterFromPersistentComponentState] + [PersistentState] public IEnumerable? Forecasts { get; set; } protected override async Task OnInitializedAsync() diff --git a/aspnetcore/blazor/state-management/prerendered-state-persistence.md b/aspnetcore/blazor/state-management/prerendered-state-persistence.md index 79e791748901..857684e173ec 100644 --- a/aspnetcore/blazor/state-management/prerendered-state-persistence.md +++ b/aspnetcore/blazor/state-management/prerendered-state-persistence.md @@ -36,13 +36,13 @@ To retain the initial value of the counter during prerendering, Blazor supports -To preserve prerendered state, use the `[SupplyParameterFromPersistentComponentState]` attribute to persist state in properties. Properties with this attribute are automatically persisted using the service during prerendering. The state is retrieved when the component renders interactively or the service is instantiated. +To preserve prerendered state, use the `[PersistentState]` attribute to persist state in properties. Properties with this attribute are automatically persisted using the service during prerendering. The state is retrieved when the component renders interactively or the service is instantiated. By default, properties are serialized using the serializer with default settings and persisted in the prerendered HTML. Serialization isn't trimmer safe and requires preservation of the types used. For more information, see . The following counter component persists counter state during prerendering and retrieves the state to initialize the component: -* The `[SupplyParameterFromPersistentComponentState]` attribute is applied to the `CounterState` type (`State`). +* The `[PersistentState]` attribute is applied to the `CounterState` type (`State`). * The counter's state is assigned when `null` in `OnInitialized` and restored automatically when the component renders interactively. `PrerenderedCounter2.razor`: @@ -60,7 +60,7 @@ The following counter component persists counter state during prerendering and r @code { - [SupplyParameterFromPersistentComponentState] + [PersistentState] public CounterState? State { get; set; } protected override void OnInitialized() @@ -109,7 +109,7 @@ The following counter component persists counter state during prerendering and r @code { - [SupplyParameterFromPersistentComponentState] + [PersistentState] public int? CurrentCount { get; set; } protected override void OnInitialized() @@ -142,7 +142,7 @@ When the component executes, `CurrentCount` is only set once during prerendering In the following example that serializes state for multiple components of the same type: -* Properties annotated with the `[SupplyParameterFromPersistentComponentState]` attribute are serialized during prerendering. +* Properties annotated with the `[PersistentState]` attribute are serialized during prerendering. * The [`@key` directive attribute](xref:blazor/components/key#use-of-the-key-directive-attribute) is used to ensure that the state is correctly associated with the component instance. * The `Element` property is initialized in the [`OnInitialized` lifecycle method](xref:blazor/components/lifecycle#component-initialization-oninitializedasync) to avoid null reference exceptions, similarly to how null references are avoided for query parameters and form data. @@ -155,7 +155,7 @@ In the following example that serializes state for multiple components of the sa @code { - [SupplyParameterFromPersistentComponentState] + [PersistentState] public State Element { get; set; } protected override void OnInitialized() @@ -188,12 +188,12 @@ In the following example that serializes state for multiple components of the sa In the following example that serializes state for a dependency injection service: -* Properties annotated with the `[SupplyParameterFromPersistentComponentState]` attribute are serialized during prerendering and deserialized when the app becomes interactive. +* Properties annotated with the `[PersistentState]` attribute are serialized during prerendering and deserialized when the app becomes interactive. * The extension method is used to register the service for persistence. The render mode is required because the render mode can't be inferred from the service type. Use any of the following values: * `RenderMode.Server`: The service is available for the Interactive Server render mode. * `RenderMode.Webassembly`: The service is available for the Interactive Webassembly render mode. * `RenderMode.InteractiveAuto`: The service is available for both the Interactive Server and Interactive Webassembly render modes if a component renders in either of those modes. -* The service is resolved during the initialization of an interactive render mode, and the properties annotated with the `[SupplyParameterFromPersistentComponentState]` attribute are deserialized. +* The service is resolved during the initialization of an interactive render mode, and the properties annotated with the `[PersistentState]` attribute are deserialized. > [!NOTE] > Only persisting scoped services is supported. @@ -205,7 +205,7 @@ In the following example that serializes state for a dependency injection servic ```csharp public class CounterService { - [SupplyParameterFromPersistentComponentState] + [PersistentState] public int CurrentCount { get; set; } public void IncrementCount() @@ -229,7 +229,7 @@ Serialized properties are identified from the actual service instance: * Supports shared code in different assemblies. * Results in each instance exposing the same properties. -As an alternative to using the declarative model for persisting state with the `[SupplyParameterFromPersistentComponentState]` attribute, you can use the service directly, which offers greater flexibility for complex state persistence scenarios. Call to register a callback to persist the component state during prerendering. The state is retrieved when the component renders interactively. Make the call at the end of initialization code in order to avoid a potential race condition during app shutdown. +As an alternative to using the declarative model for persisting state with the `[PersistentState]` attribute, you can use the service directly, which offers greater flexibility for complex state persistence scenarios. Call to register a callback to persist the component state during prerendering. The state is retrieved when the component renders interactively. Make the call at the end of initialization code in order to avoid a potential race condition during app shutdown. The following counter component example persists counter state during prerendering and retrieves the state to initialize the component. diff --git a/aspnetcore/blazor/state-management/server.md b/aspnetcore/blazor/state-management/server.md index fc340a5997f6..2442e59cf01b 100644 --- a/aspnetcore/blazor/state-management/server.md +++ b/aspnetcore/blazor/state-management/server.md @@ -58,14 +58,10 @@ During server-side rendering, Blazor Web Apps can persist a user's session (circ * Network interruptions * Proactive resource management (pausing inactive circuits) - When the component executes, `CurrentCount` is only set once during prerendering. The value is restored when the component is rerendered. The following is example output. From ee533ded393c2a54936adfc3cf9269e5b6cb70de Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Mon, 11 Aug 2025 10:10:49 -0400 Subject: [PATCH 04/16] Not Found updates --- aspnetcore/blazor/components/layouts.md | 24 ++--- aspnetcore/blazor/components/render-modes.md | 14 ++- aspnetcore/blazor/fundamentals/routing.md | 88 ++++++++++++++----- .../aspnetcore-10/includes/blazor.md | 68 +++++++++++--- 4 files changed, 141 insertions(+), 53 deletions(-) diff --git a/aspnetcore/blazor/components/layouts.md b/aspnetcore/blazor/components/layouts.md index 9ad6b6d89084..0c1ef4732a9b 100644 --- a/aspnetcore/blazor/components/layouts.md +++ b/aspnetcore/blazor/components/layouts.md @@ -304,31 +304,17 @@ Specifying the layout as a default layout in the component. You can use a in any Razor component. The following example sets a layout component named `ErrorLayout` for the `MainLayout` component's template (`...`). +To set a layout for arbitrary Razor template content, specify the layout with a component. You can use a in any Razor component. The following example sets a layout component named `ErrorLayout` for the component that includes the following Razor markup. ```razor - - - ... - - - -

Page not found

-

Sorry, there's nothing at this address.

-
-
-
+ +

Page not found

+

Sorry, there's nothing at this address.

+
``` You may need to identity the layout's namespace depending on the .NET version and type of Blazor app. For more information, see the [Make the layout namespace available](#make-the-layout-namespace-available) section. -:::moniker range=">= aspnetcore-8.0" - -> [!IMPORTANT] -> Blazor Web Apps don't use the parameter (`...` markup), but the parameter is supported for backward compatibility to avoid a breaking change in the framework. The server-side ASP.NET Core middleware pipeline processes requests on the server. Use server-side techniques to handle bad requests. For more information, see . - -:::moniker-end - :::moniker range="= aspnetcore-5.0" [!INCLUDE[](~/blazor/includes/prefer-exact-matches.md)] diff --git a/aspnetcore/blazor/components/render-modes.md b/aspnetcore/blazor/components/render-modes.md index 94e3513cf3a8..40bc7c8150cb 100644 --- a/aspnetcore/blazor/components/render-modes.md +++ b/aspnetcore/blazor/components/render-modes.md @@ -339,11 +339,21 @@ In the following example, there's no designation for the component's render mode If using the preceding component locally in a Blazor Web App, place the component in the server project's `Components/Pages` folder. The server project is the solution's project with a name that doesn't end in `.Client`. When the app is running, navigate to `/render-mode-1` in the browser's address bar. +:::moniker range=">= aspnetcore-10.0" + +During static SSR, Razor component page requests are processed by server-side ASP.NET Core middleware pipeline request processing for authorization. Dedicated Blazor features for authorization aren't operational because Razor components aren't rendered during server-side request processing. Blazor router features in the `Routes` component that aren't available during static SSR include displaying [Not Authorized content (`...`)](xref:blazor/security/index#authorizeview-component) (). Blazor Web Apps typically process unauthorized requests on the server by [customizing the behavior of Authorization Middleware](xref:security/authorization/authorizationmiddlewareresulthandler). + +:::moniker-end + +:::moniker range="< aspnetcore-10.0" + During static SSR, Razor component page requests are processed by server-side ASP.NET Core middleware pipeline request processing for routing and authorization. Dedicated Blazor features for routing and authorization aren't operational because Razor components aren't rendered during server-side request processing. Blazor router features in the `Routes` component that aren't available during static SSR include displaying: -* [Not authorized content (`...`)](xref:blazor/security/index#authorizeview-component) (): Blazor Web Apps typically process unauthorized requests on the server by [customizing the behavior of Authorization Middleware](xref:security/authorization/authorizationmiddlewareresulthandler). +* [Not Authorized content (`...`)](xref:blazor/security/index#authorizeview-component) (): Blazor Web Apps typically process unauthorized requests on the server by [customizing the behavior of Authorization Middleware](xref:security/authorization/authorizationmiddlewareresulthandler). + +* [Not Found content (`...`)](xref:blazor/fundamentals/routing#provide-custom-content-when-content-isnt-found) (): Blazor Web Apps typically process bad URL requests on the server by either displaying the browser's built-in 404 UI or returning a custom 404 page (or other response) via ASP.NET Core middleware (for example, [`UseStatusCodePagesWithRedirects`](xref:fundamentals/error-handling#usestatuscodepageswithredirects) / [API documentation](xref:Microsoft.AspNetCore.Builder.StatusCodePagesExtensions.UseStatusCodePagesWithRedirects%2A)). -* [Not found content (`...`)](xref:blazor/fundamentals/routing#provide-custom-content-when-content-isnt-found) (): Blazor Web Apps typically process bad URL requests on the server by either displaying the browser's built-in 404 UI or returning a custom 404 page (or other response) via ASP.NET Core middleware (for example, [`UseStatusCodePagesWithRedirects`](xref:fundamentals/error-handling#usestatuscodepageswithredirects) / [API documentation](xref:Microsoft.AspNetCore.Builder.StatusCodePagesExtensions.UseStatusCodePagesWithRedirects%2A)). For more information, see [Create simpler way to show Not Found 404 UI with Blazor (`dotnet/aspnetcore` #45654)](https://github.com/dotnet/aspnetcore/issues/45654). +:::moniker-end If the app exhibits root-level interactivity, server-side ASP.NET Core request processing isn't involved after the initial static SSR, which means that the preceding Blazor features work as expected. diff --git a/aspnetcore/blazor/fundamentals/routing.md b/aspnetcore/blazor/fundamentals/routing.md index 6cd3cc53ed30..475b7313c257 100644 --- a/aspnetcore/blazor/fundamentals/routing.md +++ b/aspnetcore/blazor/fundamentals/routing.md @@ -136,6 +136,8 @@ When the component navigat :::moniker-end +:::moniker range="< aspnetcore-10.0" + ## Provide custom content when content isn't found The component allows the app to specify custom content if content isn't found for the requested route. @@ -153,14 +155,14 @@ Set custom content for the Arbitrary items are supported as content of the parameter, such as other interactive components. To apply a default layout to content, see . -:::moniker range=">= aspnetcore-8.0" +Blazor Web Apps don't use the parameter (`...` markup), but the parameter is supported† for backward compatibility in .NET 8/9 to avoid a breaking change in the framework. The server-side ASP.NET Core middleware pipeline processes requests on the server. Use server-side techniques to handle bad requests. -> [!IMPORTANT] -> Blazor Web Apps don't use the parameter (`...` markup), but the parameter is supported† for backward compatibility to avoid a breaking change in the framework. The server-side ASP.NET Core middleware pipeline processes requests on the server. Use server-side techniques to handle bad requests. -> -> †*Supported* in this context means that placing `...` markup doesn't result in an exception, but using the markup isn't effective either. -> -> For more information, including a recommended approach for handling bad requests, see . +†*Supported* in this context means that placing `...` markup doesn't result in an exception, but using the markup isn't effective either. + +For more information, see the following resources: + +* +* [](#not-found-responses-using-navigationmanager-for-static-ssr-and-global-interactive-rendering) . :::moniker-end @@ -714,24 +716,22 @@ For more information on component disposal, see [!NOTE] > The following discussion mentions that a Not Found Razor component can be assigned to the `Router` component's `NotFoundPage` parameter. The parameter works in concert with `NavigationManager.NotFound` and is described in more detail later in this section. -Streaming rendering can only render components that have a route, such as a Not Found page assignment with the `Router` component's `NotFoundPage` parameter or a [Status Code Pages Re-execution Middleware page assignment](xref:fundamentals/error-handling#usestatuscodepageswithreexecute) (). The Not Found render fragment (`...`) and the `DefaultNotFound` 404 content ("`Not found`" plain text) don't have routes, so they can't be used during streaming rendering. - -Streaming `NavigationManager.NotFound` content rendering uses (in order): +Streaming rendering can only render components that have a route, such as a [`NotFoundPage` assignment](#blazor-router-has-a-notfoundpage-parameter) (`NotFoundPage="..."`) or a [Status Code Pages Re-execution Middleware page assignment](xref:fundamentals/error-handling#usestatuscodepageswithreexecute) (). `DefaultNotFound` 404 content ("`Not found`" plain text) doesn't have a route, so it can't be used during streaming rendering. -* A `NotFoundPage` passed to the `Router` component, if present. -* A Status Code Pages Re-execution Middleware page, if configured. -* No action if neither of the preceding approaches is adopted. +> [!NOTE] +> The Not Found render fragment (`...`) isn't supported in .NET 10 or later. -Non-streaming `NavigationManager.NotFound` content rendering uses (in order): +`NavigationManager.NotFound` content rendering uses the following, regardless if the response has started or not (in order): -* A `NotFoundPage` passed to the `Router` component, if present. -* Not Found render fragment content, if present. *Not recommended in .NET 10 or later.* -* `DefaultNotFound` 404 content ("`Not found`" plain text). +* If is set, render the contents of the assigned page. +* If `Router.NotFoundPage` is set, render the assigned page. +* A Status Code Pages Re-execution Middleware page, if configured. +* No action if none of the preceding approaches are adopted. [Status Code Pages Re-execution Middleware](xref:fundamentals/error-handling#usestatuscodepageswithreexecute) with takes precedence for browser-based address routing problems, such as an incorrect URL typed into the browser's address bar or selecting a link that has no endpoint in the app. @@ -796,9 +796,53 @@ When a component is rendered with a global interactive render mode, calling `Not You can use the `OnNotFound` event for notifications when `NotFound` is invoked. The event is only fired when `NotFound` is called, not for any 404 response. For example, setting `HttpContextAccessor.HttpContext.Response.StatusCode` to `404` doesn't trigger `NotFound`/`OnNotFound`. - +Apps that implement a custom router can also use `NavigationManager.NotFound`. The custom router can render Not Found content from two sources, depending on the state of the response: + +* Regardless of the response state, the re-execution path to the page can used by passing it to : + + ```csharp + app.UseStatusCodePagesWithReExecute( + "/not-found", createScopeForStatusCodePages: true); + ``` + +* When the response has started, the can be used by subscribing to the `OnNotFoundEvent` in the router: + + ```razor + @code { + [CascadingParameter] + public HttpContext? HttpContext { get; set; } + + private void OnNotFoundEvent(object sender, NotFoundEventArgs e) + { + // Only execute the logic if HTTP response has started, + // because setting NotFoundEventArgs.Path blocks re-execution + if (HttpContext?.Response.HasStarted == false) + { + return; + } + + var type = typeof(CustomNotFoundPage); + var routeAttributes = type.GetCustomAttributes(typeof(RouteAttribute), + inherit: true); + + if (routeAttributes.Length == 0) + { + throw new InvalidOperationException($"The type {type.FullName} " + + $"doesn't have a {typeof(RouteAttribute).FullName} applied."); + } + + var routeAttribute = (RouteAttribute)routeAttributes[0]; + + if (routeAttribute.Template != null) + { + e.Path = routeAttribute.Template; + } + } + } + ``` + + + In the following example components: * The `NotFoundContext` service is injected, along with the . diff --git a/aspnetcore/release-notes/aspnetcore-10/includes/blazor.md b/aspnetcore/release-notes/aspnetcore-10/includes/blazor.md index 870b06fbf138..27901a8153cb 100644 --- a/aspnetcore/release-notes/aspnetcore-10/includes/blazor.md +++ b/aspnetcore/release-notes/aspnetcore-10/includes/blazor.md @@ -390,7 +390,9 @@ AppContext.SetSwitch( ### Blazor router has a `NotFoundPage` parameter -Blazor now provides an improved way to display a "Not Found" page when navigating to a non-existent page. You can specify a page to render when `NavigationManager.NotFound` (described in the next section) is invoked by passing a page type to the `Router` component using the `NotFoundPage` parameter. This approach is recommended over using the [`NotFound` render fragment](xref:Microsoft.AspNetCore.Components.Routing.Router.NotFound%2A) (`...`), as it supports routing, works across code Status Code Pages Re-execution Middleware, and is compatible even with non-Blazor scenarios. If both a `NotFound` render fragment and `NotFoundPage` are defined, the page specified by `NotFoundPage` takes priority. +Blazor now provides an improved way to display a "Not Found" page when navigating to a non-existent page. You can specify a page to render when `NavigationManager.NotFound` (described in the next section) is invoked by passing a page type to the `Router` component using the `NotFoundPage` parameter. The feature supports routing, works across code Status Code Pages Re-execution Middleware, and is compatible even with non-Blazor scenarios. + +The [`NotFound` render fragment](xref:Microsoft.AspNetCore.Components.Routing.Router.NotFound%2A) (`...`) isn't supported in .NET 10 or later. ```razor @@ -416,19 +418,16 @@ The now includes a `Not * **Streaming rendering**: If [enhanced navigation](xref:blazor/fundamentals/routing?view=aspnetcore-10.0#enhanced-navigation-and-form-handling) is active, [streaming rendering](xref:blazor/components/rendering#streaming-rendering) renders Not Found content without reloading the page. When enhanced navigation is blocked, the framework redirects to Not Found content with a page refresh. -Streaming rendering can only render components that have a route, such as a [`NotFoundPage` assignment](#blazor-router-has-a-notfoundpage-parameter) (`NotFoundPage="..."`) or a [Status Code Pages Re-execution Middleware page assignment](xref:fundamentals/error-handling#usestatuscodepageswithreexecute) (). The Not Found render fragment (`...`) and the `DefaultNotFound` 404 content ("`Not found`" plain text) don't have routes, so they can't be used during streaming rendering. - -Streaming `NavigationManager.NotFound` content rendering uses (in order): +Streaming rendering can only render components that have a route, such as a [`NotFoundPage` assignment](#blazor-router-has-a-notfoundpage-parameter) (`NotFoundPage="..."`) or a [Status Code Pages Re-execution Middleware page assignment](xref:fundamentals/error-handling#usestatuscodepageswithreexecute) (). `DefaultNotFound` 404 content ("`Not found`" plain text) doesn't have a route, so it can't be used during streaming rendering. -* A `NotFoundPage` passed to the `Router` component, if present. -* A Status Code Pages Re-execution Middleware page, if configured. -* No action if neither of the preceding approaches is adopted. +The Not Found render fragment (`...`) isn't supported in .NET 10 or later. -Non-streaming `NavigationManager.NotFound` content rendering uses (in order): +`NavigationManager.NotFound` content rendering uses the following, regardless if the response has started or not (in order): -* A `NotFoundPage` passed to the `Router` component, if present. -* Not Found render fragment content, if present. *Not recommended in .NET 10 or later.* -* `DefaultNotFound` 404 content ("`Not found`" plain text). +* If is set, render the contents of the assigned page. +* If `Router.NotFoundPage` is set, render the assigned page. +* A Status Code Pages Re-execution Middleware page, if configured. +* No action if none of the preceding approaches are adopted. [Status Code Pages Re-execution Middleware](xref:fundamentals/error-handling#usestatuscodepageswithreexecute) with takes precedence for browser-based address routing problems, such as an incorrect URL typed into the browser's address bar or selecting a link that has no endpoint in the app. @@ -436,6 +435,53 @@ You can use the `NavigationManager.OnNotFound` event for notifications when `Not For more information and examples, see . +### Support for Not Found responses in apps without Blazor's router + +Apps that implement a custom router can use `NavigationManager.NotFound`. The custom router can render Not Found content from two sources, depending on the state of the response: + +* Regardless of the response state, the re-execution path to the page can used by passing it to : + + ```csharp + app.UseStatusCodePagesWithReExecute( + "/not-found", createScopeForStatusCodePages: true); + ``` + +* When the response has started, the can be used by subscribing to the `OnNotFoundEvent` in the router: + + ```razor + @code { + [CascadingParameter] + public HttpContext? HttpContext { get; set; } + + private void OnNotFoundEvent(object sender, NotFoundEventArgs e) + { + // Only execute the logic if HTTP response has started, + // because setting NotFoundEventArgs.Path blocks re-execution + if (HttpContext?.Response.HasStarted == false) + { + return; + } + + var type = typeof(CustomNotFoundPage); + var routeAttributes = type.GetCustomAttributes(typeof(RouteAttribute), + inherit: true); + + if (routeAttributes.Length == 0) + { + throw new InvalidOperationException($"The type {type.FullName} " + + $"doesn't have a {typeof(RouteAttribute).FullName} applied."); + } + + var routeAttribute = (RouteAttribute)routeAttributes[0]; + + if (routeAttribute.Template != null) + { + e.Path = routeAttribute.Template; + } + } + } + ``` + ### Metrics and tracing This release introduces comprehensive metrics and tracing capabilities for Blazor apps, providing detailed observability of the component lifecycle, navigation, event handling, and circuit management. From 72fa67737c7ae2895cdbd97fa550d57642d796ab Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Mon, 11 Aug 2025 10:53:06 -0400 Subject: [PATCH 05/16] Hot Reload for WebAssembly --- aspnetcore/client-side/dotnet-interop.md | 12 ++++++++++++ .../release-notes/aspnetcore-10/includes/blazor.md | 14 +++++++++++++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/aspnetcore/client-side/dotnet-interop.md b/aspnetcore/client-side/dotnet-interop.md index 7fd7c87e37d6..5a4f0dfc7bba 100644 --- a/aspnetcore/client-side/dotnet-interop.md +++ b/aspnetcore/client-side/dotnet-interop.md @@ -427,6 +427,18 @@ dotnet serve -d:bin/$(Configuration)/{TARGET FRAMEWORK}/publish In the preceding example, the `{TARGET FRAMEWORK}` placeholder is the [target framework moniker](/dotnet/standard/frameworks). +:::moniker range=">= aspnetcore-10.0" + +To enable [Hot Reload](xref:test/hot-reload) for WebAssembly, set the `WasmEnableHotReload` MSBuild property to `true` in the app's project file: + +```xml + + true + +``` + +:::moniker-end + ### Node.js console app You can create a console app with the `wasmconsole` template, which creates an app that runs under :::no-loc text="WASM"::: as a [Node.js](https://nodejs.org/) or [V8](https://developers.google.com/apps-script/guides/v8-runtime) console app: diff --git a/aspnetcore/release-notes/aspnetcore-10/includes/blazor.md b/aspnetcore/release-notes/aspnetcore-10/includes/blazor.md index 27901a8153cb..44e91f33c917 100644 --- a/aspnetcore/release-notes/aspnetcore-10/includes/blazor.md +++ b/aspnetcore/release-notes/aspnetcore-10/includes/blazor.md @@ -617,6 +617,18 @@ During server-side rendering, Blazor Web Apps can now persist a user's session ( * Network interruptions * Proactive resource management (pausing inactive circuits) -*[Enhanced navigation](xref:blazor/fundamentals/routing#enhanced-navigation-and-form-handling) with circuit state persistence isn't currently supported but planned for a future release.* +*[Enhanced navigation](xref:blazor/fundamentals/routing?view=aspnetcore-10.0#enhanced-navigation-and-form-handling) with circuit state persistence isn't currently supported but planned for a future release.* For more information, see . + +### Hot Reload for WebAssembly + +The SDK migrated to a general purpose [Hot Reload](xref:test/hot-reload) for WebAssembly. There's a new MSBuild property `WasmEnableHotReload` that's `true` by default for the `Debug` configuration (`Configuration == "Debug"`). + +Explicitly set the value to `true` in the app's project file to enable WebAssembly Hot Reload for other configurations, such as when [running .NET from JavaScript with an app created from the `wasmbrowser` template](xref:client-side/dotnet-interop?view=aspnetcore-10.0#browser-app): + +```xml + + true + +``` From ed52fa660a60024d95b637931a15cfc97aa88ef4 Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Mon, 11 Aug 2025 11:06:33 -0400 Subject: [PATCH 06/16] PWA service worker registration to prevent caching issues --- .../blazor/progressive-web-app/index.md | 30 ++++++++++++++++--- .../aspnetcore-10/includes/blazor.md | 17 +++++++++++ 2 files changed, 43 insertions(+), 4 deletions(-) diff --git a/aspnetcore/blazor/progressive-web-app/index.md b/aspnetcore/blazor/progressive-web-app/index.md index 57e9cd4581b8..7ecd728e9179 100644 --- a/aspnetcore/blazor/progressive-web-app/index.md +++ b/aspnetcore/blazor/progressive-web-app/index.md @@ -147,14 +147,36 @@ In the app's `wwwroot/index.html` file: :::moniker-end -* Add the following ` - + ``` + The [`updateViaCache: 'none'` option](https://developer.mozilla.org/docs/Web/API/ServiceWorkerRegistration/updateViaCache) ensures that: + + * The browser doesn't use cached versions of the service worker script. + * Service worker updates are applied reliably without being blocked by HTTP caching. + * PWA applications can update their service workers more predictably. + + This addresses caching issues that can prevent service worker updates from being applied correctly, which is particularly important for PWAs that rely on service workers for offline functionality. + +:::moniker-end + +:::moniker range="< aspnetcore-10.0" + +* Add the following JavaScript inside the closing `` tag immediately after the `blazor.webassembly.js` script tag: + + ```html + + ``` + +:::moniker-end + ## Installation and app manifest When visiting an app created using the PWA template, users have the option of installing the app into their OS's start menu, dock, or home screen. The way this option is presented depends on the user's browser. When using desktop Chromium-based browsers, such as Edge or Chrome, an **Add** button appears within the URL bar. After the user selects the **Add** button, they receive a confirmation dialog: diff --git a/aspnetcore/release-notes/aspnetcore-10/includes/blazor.md b/aspnetcore/release-notes/aspnetcore-10/includes/blazor.md index 44e91f33c917..08ec2301fb4c 100644 --- a/aspnetcore/release-notes/aspnetcore-10/includes/blazor.md +++ b/aspnetcore/release-notes/aspnetcore-10/includes/blazor.md @@ -632,3 +632,20 @@ Explicitly set the value to `true` in the app's project file to enable WebAssemb true ``` + +### Updated PWA service worker registration to prevent caching issues + +The service worker registration in the [Blazor Progressive Web Application (PWA)](blazor/progressive-web-app/index) project template now includes the [`updateViaCache: 'none'` option](https://developer.mozilla.org/docs/Web/API/ServiceWorkerRegistration/updateViaCache), which prevents caching issues during service worker updates. + +```diff +- navigator.serviceWorker.register('service-worker.js'); ++ navigator.serviceWorker.register('service-worker.js', { updateViaCache: 'none' }); +``` + +The option ensures that: + +* The browser doesn't use cached versions of the service worker script. +* Service worker updates are applied reliably without being blocked by HTTP caching. +* PWA applications can update their service workers more predictably. + +This addresses caching issues that can prevent service worker updates from being applied correctly, which is particularly important for PWAs that rely on service workers for offline functionality. From 11caa0025e326ab429db999679b3e9b77b966a0e Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Mon, 11 Aug 2025 11:12:26 -0400 Subject: [PATCH 07/16] PWA service worker registration to prevent caching issues (part 2) --- .../blazor/progressive-web-app/index.md | 28 +++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/aspnetcore/blazor/progressive-web-app/index.md b/aspnetcore/blazor/progressive-web-app/index.md index 7ecd728e9179..df5309088000 100644 --- a/aspnetcore/blazor/progressive-web-app/index.md +++ b/aspnetcore/blazor/progressive-web-app/index.md @@ -247,14 +247,38 @@ The cache-first strategy is valuable because: * **It ensures correctness.** When building a cache of offline resources, the service worker uses content hashing to guarantee it has fetched a complete and self-consistent snapshot of resources at a single instant in time. This cache is then used as an atomic unit. There's no point asking the network for newer resources, since the only versions required are the ones already cached. Anything else risks inconsistency and incompatibility (for example, trying to use versions of .NET assemblies that weren't compiled together). -If you must prevent the browser from fetching `service-worker-assets.js` from its HTTP cache, for example to resolve temporary integrity check failures when deploying a new version of the service worker, update the service worker registration in `wwwroot/index.html` with [`updateViaCache`](https://developer.mozilla.org/docs/Web/API/ServiceWorkerRegistration/updateViaCache) set to 'none': +:::moniker range=">= aspnetcore-10.0" + +To prevent the browser from fetching `service-worker-assets.js` from its HTTP cache, for example to resolve temporary integrity check failures when deploying a new version of the service worker, the service worker registration in `wwwroot/index.html` uses [`updateViaCache`](https://developer.mozilla.org/docs/Web/API/ServiceWorkerRegistration/updateViaCache) set to `none`: ```html ``` +The [`updateViaCache: 'none'` option](https://developer.mozilla.org/docs/Web/API/ServiceWorkerRegistration/updateViaCache) ensures that: + +* The browser doesn't use cached versions of the service worker script. +* Service worker updates are applied reliably without being blocked by HTTP caching. +* PWA applications can update their service workers more predictably. + +This addresses caching issues that can prevent service worker updates from being applied correctly, which is particularly important for PWAs that rely on service workers for offline functionality. + +:::moniker-end + +:::moniker range="< aspnetcore-10.0" + +If you must prevent the browser from fetching `service-worker-assets.js` from its HTTP cache, for example to resolve temporary integrity check failures when deploying a new version of the service worker, update the service worker registration in `wwwroot/index.html` with [`updateViaCache`](https://developer.mozilla.org/docs/Web/API/ServiceWorkerRegistration/updateViaCache) set to `none`: + +```html + +``` + +:::moniker-end + ### Background updates As a mental model, you can think of an offline-first PWA as behaving like a mobile app that can be installed. The app starts up immediately regardless of network connectivity, but the installed app logic comes from a point-in-time snapshot that might not be the latest version. From 5262dfd237499f7af72316f90b88140ba20f9206 Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Mon, 11 Aug 2025 11:49:53 -0400 Subject: [PATCH 08/16] Updates --- aspnetcore/blazor/fundamentals/routing.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aspnetcore/blazor/fundamentals/routing.md b/aspnetcore/blazor/fundamentals/routing.md index 475b7313c257..7751ccba24e9 100644 --- a/aspnetcore/blazor/fundamentals/routing.md +++ b/aspnetcore/blazor/fundamentals/routing.md @@ -162,7 +162,7 @@ Blazor Web Apps don't use the -* [](#not-found-responses-using-navigationmanager-for-static-ssr-and-global-interactive-rendering) . +* [Not Found responses](#not-found-responses) section :::moniker-end @@ -721,7 +721,7 @@ For more information on component disposal, see [!NOTE] > The following discussion mentions that a Not Found Razor component can be assigned to the `Router` component's `NotFoundPage` parameter. The parameter works in concert with `NavigationManager.NotFound` and is described in more detail later in this section. -Streaming rendering can only render components that have a route, such as a [`NotFoundPage` assignment](#blazor-router-has-a-notfoundpage-parameter) (`NotFoundPage="..."`) or a [Status Code Pages Re-execution Middleware page assignment](xref:fundamentals/error-handling#usestatuscodepageswithreexecute) (). `DefaultNotFound` 404 content ("`Not found`" plain text) doesn't have a route, so it can't be used during streaming rendering. +Streaming rendering can only render components that have a route, such as a `NotFoundPage` assignment (`NotFoundPage="..."`) or a [Status Code Pages Re-execution Middleware page assignment](xref:fundamentals/error-handling#usestatuscodepageswithreexecute) (). `DefaultNotFound` 404 content ("`Not found`" plain text) doesn't have a route, so it can't be used during streaming rendering. > [!NOTE] > The Not Found render fragment (`...`) isn't supported in .NET 10 or later. From 8c97e1468e83bf0f99436e30522e3204d45d93bd Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Mon, 11 Aug 2025 12:42:59 -0400 Subject: [PATCH 09/16] Serialization extensibility for persistent component state --- .../prerendered-state-persistence.md | 24 +++++++++++++++++++ .../aspnetcore-10/includes/blazor.md | 20 +++++++++++++++- 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/aspnetcore/blazor/state-management/prerendered-state-persistence.md b/aspnetcore/blazor/state-management/prerendered-state-persistence.md index e531daf69770..6114ad55b287 100644 --- a/aspnetcore/blazor/state-management/prerendered-state-persistence.md +++ b/aspnetcore/blazor/state-management/prerendered-state-persistence.md @@ -272,6 +272,30 @@ By initializing components with the same state used during prerendering, any exp The persisted prerendered state is transferred to the client, where it's used to restore the component state. During client-side rendering (CSR, `InteractiveWebAssembly`), the data is exposed to the browser and must not contain sensitive, private information. During interactive server-side rendering (interactive SSR, `InteractiveServer`), [ASP.NET Core Data Protection](xref:security/data-protection/introduction) ensures that the data is transferred securely. The `InteractiveAuto` render mode combines WebAssembly and Server interactivity, so it's necessary to consider data exposure to the browser, as in the CSR case. +:::moniker range=">= aspnetcore-10.0" + +## Serialization extensibility for persistent component state + + + +Implement a custom serializer with the `IPersistentComponentStateSerializer` interface. Without a registered custom serializer, serialization falls back to the existing JSON serialization. + +The custom serializer is registered in the app's `Program` file. In the following example, the `CustomUserSerializer` is registered for the `User` type: + +```csharp +builder.Services.AddSingleton, + CustomUserSerializer>(); +``` + +The type is automatically persisted and restored with the custom serializer: + +```razor +[PersistentState] +public User? CurrentUser { get; set; } = new(); +``` + +:::moniker-end + ## Components embedded into pages and views (Razor Pages/MVC) For components embedded into a page or view of a Razor Pages or MVC app, you must add the [Persist Component State Tag Helper](xref:mvc/views/tag-helpers/builtin-th/persist-component-state-tag-helper) with the `` HTML tag inside the closing `` tag of the app's layout. **This is only required for Razor Pages and MVC apps.** For more information, see . diff --git a/aspnetcore/release-notes/aspnetcore-10/includes/blazor.md b/aspnetcore/release-notes/aspnetcore-10/includes/blazor.md index 08ec2301fb4c..be4506932b96 100644 --- a/aspnetcore/release-notes/aspnetcore-10/includes/blazor.md +++ b/aspnetcore/release-notes/aspnetcore-10/includes/blazor.md @@ -635,7 +635,7 @@ Explicitly set the value to `true` in the app's project file to enable WebAssemb ### Updated PWA service worker registration to prevent caching issues -The service worker registration in the [Blazor Progressive Web Application (PWA)](blazor/progressive-web-app/index) project template now includes the [`updateViaCache: 'none'` option](https://developer.mozilla.org/docs/Web/API/ServiceWorkerRegistration/updateViaCache), which prevents caching issues during service worker updates. +The service worker registration in the [Blazor Progressive Web Application (PWA)](xref:blazor/progressive-web-app/index) project template now includes the [`updateViaCache: 'none'` option](https://developer.mozilla.org/docs/Web/API/ServiceWorkerRegistration/updateViaCache), which prevents caching issues during service worker updates. ```diff - navigator.serviceWorker.register('service-worker.js'); @@ -649,3 +649,21 @@ The option ensures that: * PWA applications can update their service workers more predictably. This addresses caching issues that can prevent service worker updates from being applied correctly, which is particularly important for PWAs that rely on service workers for offline functionality. + +### Serialization extensibility for persistent component state + +Implement a custom serializer with the `IPersistentComponentStateSerializer` interface. Without a registered custom serializer, serialization falls back to the existing JSON serialization. + +The custom serializer is registered in the app's `Program` file. In the following example, the `CustomUserSerializer` is registered for the `User` type: + +```csharp +builder.Services.AddSingleton, + CustomUserSerializer>(); +``` + +The type is automatically persisted and restored with the custom serializer: + +```razor +[PersistentState] +public User? CurrentUser { get; set; } = new(); +``` From 794162c5e941995f9754e8e60d41cb732627e201 Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Mon, 11 Aug 2025 12:57:13 -0400 Subject: [PATCH 10/16] OwningComponentBase now implements IAsyncDisposable --- aspnetcore/release-notes/aspnetcore-10/includes/blazor.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/aspnetcore/release-notes/aspnetcore-10/includes/blazor.md b/aspnetcore/release-notes/aspnetcore-10/includes/blazor.md index be4506932b96..a7e9a5738f22 100644 --- a/aspnetcore/release-notes/aspnetcore-10/includes/blazor.md +++ b/aspnetcore/release-notes/aspnetcore-10/includes/blazor.md @@ -667,3 +667,7 @@ The type is automatically persisted and restored with the custom serializer: [PersistentState] public User? CurrentUser { get; set; } = new(); ``` + +### `OwningComponentBase` now implements `IAsyncDisposable` + +[`OwningComponentBase`](xref:fundamentals/dependency-injection#utility-base-component-classes-to-manage-a-di-scope) now includes support for asynchronous disposal, improving resource management. There are new `DisposeAsync` and `DisposeAsyncCore` methods with an updated `Dispose` method to handle both synchronous and asynchronous disposal of the service scope. From 094242b730a36433bad45f0d43ea050a5861e9c0 Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Mon, 11 Aug 2025 13:17:51 -0400 Subject: [PATCH 11/16] InputHidden component --- aspnetcore/blazor/forms/input-components.md | 33 +++++++++++++++++++ .../aspnetcore-10/includes/blazor.md | 27 +++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/aspnetcore/blazor/forms/input-components.md b/aspnetcore/blazor/forms/input-components.md index 70b69e0e4cd1..ff1297934a50 100644 --- a/aspnetcore/blazor/forms/input-components.md +++ b/aspnetcore/blazor/forms/input-components.md @@ -556,3 +556,36 @@ Assign a custom template to The ProductionDate field has an incorrect date value. :::moniker-end + +:::moniker range=">= aspnetcore-10.0" + +## `InputHidden` component to handle hidden input fields in forms + + + +The `InputHidden` component provides a hidden input field for storing string values. + +In the following example, a hidden input field is created for the form's `Parameter` property. When the form is submitted, the value of the hidden field is displayed: + +```razor + + + + + +@if (submitted) +{ +

Hello @Parameter!

+} + +@code { + private bool submitted; + + [SupplyParameterFromForm] + public string Parameter { get; set; } = "stranger"; + + private void Submit() => submitted = true; +} +``` + +:::moniker-end diff --git a/aspnetcore/release-notes/aspnetcore-10/includes/blazor.md b/aspnetcore/release-notes/aspnetcore-10/includes/blazor.md index a7e9a5738f22..99a5f8006530 100644 --- a/aspnetcore/release-notes/aspnetcore-10/includes/blazor.md +++ b/aspnetcore/release-notes/aspnetcore-10/includes/blazor.md @@ -671,3 +671,30 @@ public User? CurrentUser { get; set; } = new(); ### `OwningComponentBase` now implements `IAsyncDisposable` [`OwningComponentBase`](xref:fundamentals/dependency-injection#utility-base-component-classes-to-manage-a-di-scope) now includes support for asynchronous disposal, improving resource management. There are new `DisposeAsync` and `DisposeAsyncCore` methods with an updated `Dispose` method to handle both synchronous and asynchronous disposal of the service scope. + +### New `InputHidden` component to handle hidden input fields in forms + +The new `InputHidden` component provides a hidden input field for storing string values. + +In the following example, a hidden input field is created for the form's `Parameter` property. When the form is submitted, the value of the hidden field is displayed: + +```razor + + + + + +@if (submitted) +{ +

Hello @Parameter!

+} + +@code { + private bool submitted; + + [SupplyParameterFromForm] + public string Parameter { get; set; } = "stranger"; + + private void Submit() => submitted = true; +} +``` From acbc346da3f482adfbb2fd62098b884d960fcf5b Mon Sep 17 00:00:00 2001 From: Luke Latham <1622880+guardrex@users.noreply.github.com> Date: Mon, 11 Aug 2025 13:39:15 -0400 Subject: [PATCH 12/16] Update aspnetcore/release-notes/aspnetcore-10/includes/blazor.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- aspnetcore/release-notes/aspnetcore-10/includes/blazor.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/aspnetcore/release-notes/aspnetcore-10/includes/blazor.md b/aspnetcore/release-notes/aspnetcore-10/includes/blazor.md index 99a5f8006530..de67eb042f0e 100644 --- a/aspnetcore/release-notes/aspnetcore-10/includes/blazor.md +++ b/aspnetcore/release-notes/aspnetcore-10/includes/blazor.md @@ -420,8 +420,6 @@ The now includes a `Not Streaming rendering can only render components that have a route, such as a [`NotFoundPage` assignment](#blazor-router-has-a-notfoundpage-parameter) (`NotFoundPage="..."`) or a [Status Code Pages Re-execution Middleware page assignment](xref:fundamentals/error-handling#usestatuscodepageswithreexecute) (). `DefaultNotFound` 404 content ("`Not found`" plain text) doesn't have a route, so it can't be used during streaming rendering. -The Not Found render fragment (`...`) isn't supported in .NET 10 or later. - `NavigationManager.NotFound` content rendering uses the following, regardless if the response has started or not (in order): * If is set, render the contents of the assigned page. From e1bac19902b5518388606fe5ee41935505041075 Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Tue, 12 Aug 2025 08:39:30 -0400 Subject: [PATCH 13/16] Updates --- aspnetcore/blazor/debug.md | 24 +++++++++++++ .../blazor/progressive-web-app/index.md | 30 ---------------- aspnetcore/client-side/dotnet-interop.md | 36 ++++++++++++------- aspnetcore/mvc/controllers/routing.md | 4 +-- .../aspnetcore-10/includes/blazor.md | 16 +++++++-- 5 files changed, 63 insertions(+), 47 deletions(-) diff --git a/aspnetcore/blazor/debug.md b/aspnetcore/blazor/debug.md index 037712630d29..b47fbb5c16a9 100644 --- a/aspnetcore/blazor/debug.md +++ b/aspnetcore/blazor/debug.md @@ -516,3 +516,27 @@ VsRegEdit.exe set "" HKCU JSDebugger\Options\Debugging "BlazorT ``` The `{TIMEOUT}` placeholder in the preceding command is in milliseconds. For example, one minute is assigned as `60000`. + +:::moniker range=">= aspnetcore-10.0" + +## Control Hot Reload + +The `WasmEnableHotReload` MSBuild property enables [Hot Reload](xref:test/hot-reload) and is set to `true` by default when building in the `Debug` configuration. Hot Reload isn't enabled (set to `false`) when building in any other configuration. + +To use a custom configuration name when debugging, for example, `DebugWebAssembly`, set the property to `true` to enable Hot Reload: + +```xml + + true + +``` + +To disable Hot Reload for the `Debug` configuration, set the value to `false`: + +```xml + + false + +``` + +:::moniker-end diff --git a/aspnetcore/blazor/progressive-web-app/index.md b/aspnetcore/blazor/progressive-web-app/index.md index df5309088000..8414915e0e00 100644 --- a/aspnetcore/blazor/progressive-web-app/index.md +++ b/aspnetcore/blazor/progressive-web-app/index.md @@ -147,8 +147,6 @@ In the app's `wwwroot/index.html` file: :::moniker-end -:::moniker range=">= aspnetcore-10.0" - * Add the following JavaScript inside the closing `` tag immediately after the `blazor.webassembly.js` script tag: ```html @@ -165,18 +163,6 @@ In the app's `wwwroot/index.html` file: This addresses caching issues that can prevent service worker updates from being applied correctly, which is particularly important for PWAs that rely on service workers for offline functionality. -:::moniker-end - -:::moniker range="< aspnetcore-10.0" - -* Add the following JavaScript inside the closing `` tag immediately after the `blazor.webassembly.js` script tag: - - ```html - - ``` - -:::moniker-end - ## Installation and app manifest When visiting an app created using the PWA template, users have the option of installing the app into their OS's start menu, dock, or home screen. The way this option is presented depends on the user's browser. When using desktop Chromium-based browsers, such as Edge or Chrome, an **Add** button appears within the URL bar. After the user selects the **Add** button, they receive a confirmation dialog: @@ -247,8 +233,6 @@ The cache-first strategy is valuable because: * **It ensures correctness.** When building a cache of offline resources, the service worker uses content hashing to guarantee it has fetched a complete and self-consistent snapshot of resources at a single instant in time. This cache is then used as an atomic unit. There's no point asking the network for newer resources, since the only versions required are the ones already cached. Anything else risks inconsistency and incompatibility (for example, trying to use versions of .NET assemblies that weren't compiled together). -:::moniker range=">= aspnetcore-10.0" - To prevent the browser from fetching `service-worker-assets.js` from its HTTP cache, for example to resolve temporary integrity check failures when deploying a new version of the service worker, the service worker registration in `wwwroot/index.html` uses [`updateViaCache`](https://developer.mozilla.org/docs/Web/API/ServiceWorkerRegistration/updateViaCache) set to `none`: ```html @@ -265,20 +249,6 @@ The [`updateViaCache: 'none'` option](https://developer.mozilla.org/docs/Web/API This addresses caching issues that can prevent service worker updates from being applied correctly, which is particularly important for PWAs that rely on service workers for offline functionality. -:::moniker-end - -:::moniker range="< aspnetcore-10.0" - -If you must prevent the browser from fetching `service-worker-assets.js` from its HTTP cache, for example to resolve temporary integrity check failures when deploying a new version of the service worker, update the service worker registration in `wwwroot/index.html` with [`updateViaCache`](https://developer.mozilla.org/docs/Web/API/ServiceWorkerRegistration/updateViaCache) set to `none`: - -```html - -``` - -:::moniker-end - ### Background updates As a mental model, you can think of an offline-first PWA as behaving like a mobile app that can be installed. The app starts up immediately regardless of network connectivity, but the installed app logic comes from a point-in-time snapshot that might not be the latest version. diff --git a/aspnetcore/client-side/dotnet-interop.md b/aspnetcore/client-side/dotnet-interop.md index 5a4f0dfc7bba..2b3e0060bce1 100644 --- a/aspnetcore/client-side/dotnet-interop.md +++ b/aspnetcore/client-side/dotnet-interop.md @@ -427,18 +427,6 @@ dotnet serve -d:bin/$(Configuration)/{TARGET FRAMEWORK}/publish In the preceding example, the `{TARGET FRAMEWORK}` placeholder is the [target framework moniker](/dotnet/standard/frameworks). -:::moniker range=">= aspnetcore-10.0" - -To enable [Hot Reload](xref:test/hot-reload) for WebAssembly, set the `WasmEnableHotReload` MSBuild property to `true` in the app's project file: - -```xml - - true - -``` - -:::moniker-end - ### Node.js console app You can create a console app with the `wasmconsole` template, which creates an app that runs under :::no-loc text="WASM"::: as a [Node.js](https://nodejs.org/) or [V8](https://developers.google.com/apps-script/guides/v8-runtime) console app: @@ -469,6 +457,30 @@ node bin/$(Configuration)/{TARGET FRAMEWORK}/{PATH}/main.mjs In the preceding example, the `{TARGET FRAMEWORK}` placeholder is the [target framework moniker](/dotnet/standard/frameworks), and the `{PATH}` placeholder is the path to the `main.mjs` file. +:::moniker range=">= aspnetcore-10.0" + +## Control Hot Reload + +The `WasmEnableHotReload` MSBuild property enables [Hot Reload](xref:test/hot-reload) and is set to `true` by default when building in the `Debug` configuration. Hot Reload isn't enabled (set to `false`) when building in any other configuration. + +To use a custom configuration name when debugging, for example, `DebugWebAssembly`, set the property to `true` to enable Hot Reload: + +```xml + + true + +``` + +To disable Hot Reload for the `Debug` configuration, set the value to `false`: + +```xml + + false + +``` + +:::moniker-end + ## Additional resources * [Configuring and hosting .NET WebAssembly applications](https://github.com/dotnet/runtime/blob/main/src/mono/wasm/features.md) diff --git a/aspnetcore/mvc/controllers/routing.md b/aspnetcore/mvc/controllers/routing.md index f31c370d2a30..e24d8ff49618 100644 --- a/aspnetcore/mvc/controllers/routing.md +++ b/aspnetcore/mvc/controllers/routing.md @@ -389,7 +389,7 @@ The [[Consumes]](xref:Microsoft.AspNetCore.Mvc.ConsumesAttribute) attribute allo See [Routing](xref:fundamentals/routing) for a full description of route templates and related options. -For more information on `[ApiController]`, see [ApiController attribute](xref:web-api/index##apicontroller-attribute). +For more information on `[ApiController]`, see [ApiController attribute](xref:web-api/index#apicontroller-attribute). ## Route name @@ -1231,7 +1231,7 @@ The [[Consumes]](xref:Microsoft.AspNetCore.Mvc.ConsumesAttribute) attribute allo See [Routing](xref:fundamentals/routing) for a full description of route templates and related options. -For more information on `[ApiController]`, see [ApiController attribute](xref:web-api/index##apicontroller-attribute). +For more information on `[ApiController]`, see [ApiController attribute](xref:web-api/index#apicontroller-attribute). ## Route name diff --git a/aspnetcore/release-notes/aspnetcore-10/includes/blazor.md b/aspnetcore/release-notes/aspnetcore-10/includes/blazor.md index de67eb042f0e..08aa68fcc996 100644 --- a/aspnetcore/release-notes/aspnetcore-10/includes/blazor.md +++ b/aspnetcore/release-notes/aspnetcore-10/includes/blazor.md @@ -619,11 +619,11 @@ During server-side rendering, Blazor Web Apps can now persist a user's session ( For more information, see . -### Hot Reload for WebAssembly +### Hot Reload for Blazor WebAssembly and .NET on WebAssembly -The SDK migrated to a general purpose [Hot Reload](xref:test/hot-reload) for WebAssembly. There's a new MSBuild property `WasmEnableHotReload` that's `true` by default for the `Debug` configuration (`Configuration == "Debug"`). +The SDK migrated to a general purpose [Hot Reload](xref:test/hot-reload) for WebAssembly scenarios. There's a new MSBuild property `WasmEnableHotReload` that's `true` by default for the `Debug` configuration (`Configuration == "Debug"`) that enables Hot Reload. -Explicitly set the value to `true` in the app's project file to enable WebAssembly Hot Reload for other configurations, such as when [running .NET from JavaScript with an app created from the `wasmbrowser` template](xref:client-side/dotnet-interop?view=aspnetcore-10.0#browser-app): +For other configurations with custom configuration names, set the value to `true` in the app's project file to enable Hot Reload: ```xml @@ -631,6 +631,14 @@ Explicitly set the value to `true` in the app's project file to enable WebAssemb ``` +To disable Hot Reload for the `Debug` configuration, set the value to `false`: + +```xml + + false + +``` + ### Updated PWA service worker registration to prevent caching issues The service worker registration in the [Blazor Progressive Web Application (PWA)](xref:blazor/progressive-web-app/index) project template now includes the [`updateViaCache: 'none'` option](https://developer.mozilla.org/docs/Web/API/ServiceWorkerRegistration/updateViaCache), which prevents caching issues during service worker updates. @@ -648,6 +656,8 @@ The option ensures that: This addresses caching issues that can prevent service worker updates from being applied correctly, which is particularly important for PWAs that rely on service workers for offline functionality. +We recommend using the option set to `none` in all PWAs, including those that target .NET 9 or earlier. + ### Serialization extensibility for persistent component state Implement a custom serializer with the `IPersistentComponentStateSerializer` interface. Without a registered custom serializer, serialization falls back to the existing JSON serialization. From 1965981269571311cf00992e8771997560bee621 Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Tue, 12 Aug 2025 15:05:54 -0400 Subject: [PATCH 14/16] Passkeys blurb update --- aspnetcore/release-notes/aspnetcore-10/includes/blazor.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aspnetcore/release-notes/aspnetcore-10/includes/blazor.md b/aspnetcore/release-notes/aspnetcore-10/includes/blazor.md index 08aa68fcc996..70ef276a6ce6 100644 --- a/aspnetcore/release-notes/aspnetcore-10/includes/blazor.md +++ b/aspnetcore/release-notes/aspnetcore-10/includes/blazor.md @@ -602,9 +602,9 @@ For more information, see Date: Tue, 12 Aug 2025 15:28:16 -0400 Subject: [PATCH 15/16] Add .NET CLI command to passkey blurb --- aspnetcore/release-notes/aspnetcore-10/includes/blazor.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/aspnetcore/release-notes/aspnetcore-10/includes/blazor.md b/aspnetcore/release-notes/aspnetcore-10/includes/blazor.md index 70ef276a6ce6..04db7a84d861 100644 --- a/aspnetcore/release-notes/aspnetcore-10/includes/blazor.md +++ b/aspnetcore/release-notes/aspnetcore-10/includes/blazor.md @@ -602,7 +602,11 @@ For more information, see Date: Tue, 12 Aug 2025 15:50:09 -0400 Subject: [PATCH 16/16] Apply suggestions from code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- aspnetcore/blazor/fundamentals/routing.md | 5 ++--- aspnetcore/release-notes/aspnetcore-10/includes/blazor.md | 3 +-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/aspnetcore/blazor/fundamentals/routing.md b/aspnetcore/blazor/fundamentals/routing.md index 7751ccba24e9..cee45dab7dba 100644 --- a/aspnetcore/blazor/fundamentals/routing.md +++ b/aspnetcore/blazor/fundamentals/routing.md @@ -822,13 +822,12 @@ Apps that implement a custom router can also use `NavigationManager.NotFound`. T } var type = typeof(CustomNotFoundPage); - var routeAttributes = type.GetCustomAttributes(typeof(RouteAttribute), - inherit: true); + var routeAttributes = type.GetCustomAttributes(inherit: true); if (routeAttributes.Length == 0) { throw new InvalidOperationException($"The type {type.FullName} " + - $"doesn't have a {typeof(RouteAttribute).FullName} applied."); + $"doesn't have a {nameof(RouteAttribute)} applied."); } var routeAttribute = (RouteAttribute)routeAttributes[0]; diff --git a/aspnetcore/release-notes/aspnetcore-10/includes/blazor.md b/aspnetcore/release-notes/aspnetcore-10/includes/blazor.md index 04db7a84d861..a1d7cc99dfbe 100644 --- a/aspnetcore/release-notes/aspnetcore-10/includes/blazor.md +++ b/aspnetcore/release-notes/aspnetcore-10/includes/blazor.md @@ -461,8 +461,7 @@ Apps that implement a custom router can use `NavigationManager.NotFound`. The cu } var type = typeof(CustomNotFoundPage); - var routeAttributes = type.GetCustomAttributes(typeof(RouteAttribute), - inherit: true); + var routeAttributes = type.GetCustomAttributes(inherit: true); if (routeAttributes.Length == 0) {