From 51026165cffb608f0b8d22b3daa33116a2d5be04 Mon Sep 17 00:00:00 2001 From: Luke Latham <1622880+guardrex@users.noreply.github.com> Date: Tue, 12 Aug 2025 16:17:55 -0400 Subject: [PATCH 1/3] Blazor Preview 7 coverage (#35919) --- aspnetcore/blazor/components/integration.md | 6 +- aspnetcore/blazor/components/layouts.md | 24 +-- aspnetcore/blazor/components/lifecycle.md | 2 +- aspnetcore/blazor/components/render-modes.md | 14 +- aspnetcore/blazor/debug.md | 24 +++ aspnetcore/blazor/forms/input-components.md | 33 ++++ aspnetcore/blazor/fundamentals/routing.md | 87 ++++++--- .../blazor/host-and-deploy/server/index.md | 6 +- .../blazor/progressive-web-app/index.md | 28 ++- aspnetcore/blazor/security/index.md | 2 +- .../prerendered-state-persistence.md | 92 ++++------ aspnetcore/blazor/state-management/server.md | 24 +-- aspnetcore/client-side/dotnet-interop.md | 24 +++ aspnetcore/mvc/controllers/routing.md | 4 +- .../aspnetcore-10/includes/blazor.md | 173 ++++++++++++++++-- 15 files changed, 393 insertions(+), 150 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/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/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/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/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/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/blazor/fundamentals/routing.md b/aspnetcore/blazor/fundamentals/routing.md index 6cd3cc53ed30..cee45dab7dba 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](#not-found-responses) section :::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 (`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,52 @@ 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(inherit: true); + + if (routeAttributes.Length == 0) + { + throw new InvalidOperationException($"The type {type.FullName} " + + $"doesn't have a {nameof(RouteAttribute)} 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/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/blazor/progressive-web-app/index.md b/aspnetcore/blazor/progressive-web-app/index.md index 57e9cd4581b8..8414915e0e00 100644 --- a/aspnetcore/blazor/progressive-web-app/index.md +++ b/aspnetcore/blazor/progressive-web-app/index.md @@ -147,14 +147,22 @@ 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. + ## 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: @@ -225,14 +233,22 @@ 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': +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. + ### 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/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..6114ad55b287 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 nullable `int` type (`CurrentCount`). * The counter's state is assigned when `null` in `OnInitialized` and restored automatically when the component renders interactively. `PrerenderedCounter2.razor`: @@ -55,61 +55,12 @@ The following counter component persists counter state during prerendering and r

Prerendered Counter 2

-

Current count: @State?.CurrentCount

- - - -@code { - [SupplyParameterFromPersistentComponentState] - public CounterState? State { get; set; } - - protected override void OnInitialized() - { - if (State is null) - { - State = new() { CurrentCount = Random.Shared.Next(100) }; - Logger.LogInformation("CurrentCount set to {Count}", - State.CurrentCount); - } - else - { - Logger.LogInformation("CurrentCount restored to {Count}", - State.CurrentCount); - } - } - - private void IncrementCount() - { - if (State is not null) - { - State.CurrentCount++; - } - } - - public class CounterState - { - public int CurrentCount { get; set; } - } -} -``` - - 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. @@ -142,7 +92,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 +105,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 +138,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 +155,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 +179,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. @@ -322,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/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) -