From 8da8d105e3780b21f79aeabba842c72c39e9f914 Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Fri, 17 May 2024 15:02:24 -0400 Subject: [PATCH 1/3] Static SSR pages in a globally-interactive app --- aspnetcore/blazor/components/render-modes.md | 67 +++++++++++++++---- aspnetcore/blazor/security/server/index.md | 2 +- .../aspnetcore-9/includes/blazor.md | 47 +++++++++++++ 3 files changed, 101 insertions(+), 15 deletions(-) diff --git a/aspnetcore/blazor/components/render-modes.md b/aspnetcore/blazor/components/render-modes.md index 636ed4560906..f36a5136c07e 100644 --- a/aspnetcore/blazor/components/render-modes.md +++ b/aspnetcore/blazor/components/render-modes.md @@ -181,26 +181,19 @@ For more information, see . Properties and fields can assign a render mode. -The second approach described in this section, setting the render mode by component instance, is especially useful when your app specification calls for either of the following scenarios: - -* You have an area (folder) of the app with components that must adopt static server-side rendering (static SSR) and only run on the server. The app controls the render mode globally by setting the render mode on the `Routes` component in the `App` component based on the path to the folder. -* You have components around the app in various locations (not in a single folder) that must adopt static SSR and only run on the server. The app controls the render mode on a per-component basis by setting the render mode with the `@rendermode` directive in component instances. Reflection is used in the `App` component to set the render mode on the `Routes` component. - -In both cases, the component that must adopt static SSR must also force a full-page reload. - -The preceding two scenarios are covered with examples in the [Fine control of render modes](#fine-control-of-render-modes) section later in this article. The following two subsections focus on basic approaches for setting the render mode. +The second approach described in this section, setting the render mode by component instance, is especially useful when your app specification calls for one or more components to adopt static SSR in a globally-interactive app. This scenario is covered in the [Static SSR pages in a globally-interactive app](#static-ssr-pages-in-a-globally-interactive-app) section later in this article. The following two subsections focus on basic approaches for setting the render mode. ### Set the render mode by component definition A component definition can define a render mode via a private field: ```razor -@rendermode renderModeForPage +@rendermode pageRenderMode ... @code { - private static IComponentRenderMode renderModeForPage = InteractiveServer; + private static IComponentRenderMode pageRenderMode = InteractiveServer; } ``` @@ -209,16 +202,16 @@ A component definition can define a render mode via a private field: The following example applies interactive server-side rendering (interactive SSR) to any request. ```razor - + ... @code { - private IComponentRenderMode? RenderModeForPage => InteractiveServer; + private IComponentRenderMode? PageRenderMode => InteractiveServer; } ``` -Additional information on render mode propagation is provided in the [Render mode propagation](#render-mode-propagation) section later in this article. The [Fine control of render modes](#fine-control-of-render-modes) section shows how to use the preceding approach to adopt static SSR in either specific areas of the app (folders) or for specific components spread around the app with per-component render mode assignments. +Additional information on render mode propagation is provided in the [Render mode propagation](#render-mode-propagation) section later in this article. The [Static SSR pages in a globally-interactive app](#static-ssr-pages-in-a-globally-interactive-app) section shows how to use the preceding approach to adopt static SSR in a globally-interactive app. ## Prerendering @@ -520,10 +513,54 @@ The following component results in a runtime error when the component is rendere > :::no-loc text="Cannot create a component of type 'BlazorSample.Components.SharedMessage' because its render mode 'Microsoft.AspNetCore.Components.Web.InteractiveWebAssemblyRenderMode' is not supported by Interactive Server rendering."::: -## Fine control of render modes +## Static SSR pages in a globally-interactive app There are cases where the app's specification calls for components to adopt static server-side rendering (static SSR) and only run on the server, while the rest of the app uses an interactive render mode. +:::moniker range=">= aspnetcore-9.0" + +Mark any Razor component page with the `[ExcludeFromInteractiveRouting]` attribute assigned with the `@attribute` Razor directive: + +```razor +@attribute [ExcludeFromInteractiveRouting] +``` + +Applying the attribute causes navigation to the page to exit from interactive routing. That is, inbound navigation is forced to perform a full-page reload instead resolving the page via SPA-style interactive routing. This means that your top-level root component, typically the `App` component (`App.razor`), re-runs, allowing you to switch to a different top-level render mode. + +In your `App` component, you can use the following pattern, where all pages default to the `InteractiveServer` render mode, retaining global interactivity, except for pages annotated with `[ExcludeFromInteractiveRouting]`, which only render with static SSR. Of course, you can replace `InteractiveServer` with `InteractiveWebAssembly` or `InteractiveAuto` to specify a different default global mode. + +```razor + + + + ... other head content here ... + + + + + + + + +@code { + [CascadingParameter] + private HttpContext HttpContext { get; set; } = default!; + + private IComponentRenderMode? PageRenderMode + => HttpContext.AcceptsInteractiveRouting() ? InteractiveServer : null; +} +``` + +The `HttpContext.AcceptsInteractiveRouting` extension method allows the component to detect whether `[ExcludeFromInteractiveRouting]` is applied to the current page. Alternatively, you can read endpoint metadata manually using `HttpContext.GetEndpoint()?.Metadata`. + +This approach is useful only if you have certain pages that can't work with interactive Server or WebAssembly rendering. For example, adopt this approach for pages that include code that depends on reading/writing HTTP cookies and can only work in a request/response cycle. Forcing those pages to use static SSR mode forces them into this traditional request/response cycle instead of interactive SPA-style rendering. + +For pages that work with interactive SPA-style rendering, you shouldn't force them to use static SSR rendering, as it's less efficient and less responsive for the end user. + +:::moniker-end + +:::moniker range="< aspnetcore-9.0" + There are two approaches that can be taken for fine control of render modes, each of which is described in the following subsections: * [Area (folder) of static SSR components](#area-folder-of-static-ssr-components): You have an area (folder) of the app with components that must adopt static SSR and share the same route path prefix. The app controls the render mode globally by setting the render mode on the `Routes` component in the `App` component based on the path to the folder. @@ -687,6 +724,8 @@ Interactive components around the app ***avoid*** applying the custom static SSR In the preceding code, change the `{INTERACTIVE RENDER MODE}` placeholder to the appropriate value, depending on if the component should adopt , , or rendering. +:::moniker-end + ## Client-side services fail to resolve during prerendering Assuming that prerendering isn't disabled for a component or for the app, a component in the `.Client` project is prerendered on the server. Because the server doesn't have access to registered client-side Blazor services, it isn't possible to inject these services into a component without receiving an error that the service can't be found during prerendering. diff --git a/aspnetcore/blazor/security/server/index.md b/aspnetcore/blazor/security/server/index.md index 877698ba47b8..086e8ddec41b 100644 --- a/aspnetcore/blazor/security/server/index.md +++ b/aspnetcore/blazor/security/server/index.md @@ -769,7 +769,7 @@ To avoid showing unauthorized content, for example content in an [`AuthorizeView ``` - You can also selectively disable prerendering with fine control of the render mode applied to the `Routes` component instance. For more information, see . + You can also selectively disable prerendering with fine control of the render mode applied to the `Routes` component instance. For more information, see . :::moniker-end diff --git a/aspnetcore/release-notes/aspnetcore-9/includes/blazor.md b/aspnetcore/release-notes/aspnetcore-9/includes/blazor.md index 1b5dd5b740af..e4bc25ef5d54 100644 --- a/aspnetcore/release-notes/aspnetcore-9/includes/blazor.md +++ b/aspnetcore/release-notes/aspnetcore-9/includes/blazor.md @@ -1,3 +1,50 @@ + + ### Constructor injection Razor components support constructor injection. From 259f1c6a39f28082345a2d3f171ec4022410193b Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Mon, 20 May 2024 07:54:52 -0400 Subject: [PATCH 2/3] Updates --- aspnetcore/blazor/components/render-modes.md | 14 +++++++------- .../release-notes/aspnetcore-9/includes/blazor.md | 15 ++++++--------- 2 files changed, 13 insertions(+), 16 deletions(-) diff --git a/aspnetcore/blazor/components/render-modes.md b/aspnetcore/blazor/components/render-modes.md index f36a5136c07e..eada8a1f3314 100644 --- a/aspnetcore/blazor/components/render-modes.md +++ b/aspnetcore/blazor/components/render-modes.md @@ -181,7 +181,7 @@ For more information, see . Properties and fields can assign a render mode. -The second approach described in this section, setting the render mode by component instance, is especially useful when your app specification calls for one or more components to adopt static SSR in a globally-interactive app. This scenario is covered in the [Static SSR pages in a globally-interactive app](#static-ssr-pages-in-a-globally-interactive-app) section later in this article. The following two subsections focus on basic approaches for setting the render mode. +The second approach described in this section, setting the render mode by component instance, is especially useful when your app specification calls for one or more components to adopt static SSR in a globally-interactive app. This scenario is covered in the [Static SSR pages in a globally-interactive app](#static-ssr-pages-in-a-globally-interactive-app) section later in this article. ### Set the render mode by component definition @@ -525,20 +525,20 @@ Mark any Razor component page with the `[ExcludeFromInteractiveRouting]` attribu @attribute [ExcludeFromInteractiveRouting] ``` -Applying the attribute causes navigation to the page to exit from interactive routing. That is, inbound navigation is forced to perform a full-page reload instead resolving the page via SPA-style interactive routing. This means that your top-level root component, typically the `App` component (`App.razor`), re-runs, allowing you to switch to a different top-level render mode. +This approach is only useful when you have specific pages that can't work with interactive Server or WebAssembly rendering. For example, adopt this approach for pages that include code that depends on reading/writing HTTP cookies and can only work in a request/response cycle instead of interactive rendering. -In your `App` component, you can use the following pattern, where all pages default to the `InteractiveServer` render mode, retaining global interactivity, except for pages annotated with `[ExcludeFromInteractiveRouting]`, which only render with static SSR. Of course, you can replace `InteractiveServer` with `InteractiveWebAssembly` or `InteractiveAuto` to specify a different default global mode. +In the `App` component, use the following pattern, where all pages default to the `InteractiveServer` render mode, retaining global interactivity, except for pages annotated with `[ExcludeFromInteractiveRouting]`, which only render with static SSR. You can replace `InteractiveServer` with `InteractiveWebAssembly` or `InteractiveAuto` to specify a different default global render mode. ```razor - ... other head content here ... + ... - + ... @@ -553,9 +553,9 @@ In your `App` component, you can use the following pattern, where all pages defa The `HttpContext.AcceptsInteractiveRouting` extension method allows the component to detect whether `[ExcludeFromInteractiveRouting]` is applied to the current page. Alternatively, you can read endpoint metadata manually using `HttpContext.GetEndpoint()?.Metadata`. -This approach is useful only if you have certain pages that can't work with interactive Server or WebAssembly rendering. For example, adopt this approach for pages that include code that depends on reading/writing HTTP cookies and can only work in a request/response cycle. Forcing those pages to use static SSR mode forces them into this traditional request/response cycle instead of interactive SPA-style rendering. +This approach is useful only if you have specific pages that can't work with interactive Server or WebAssembly rendering. For example, adopt this approach for pages that include code that depends on reading/writing HTTP cookies and can only work in a request/response cycle. Forcing those pages to use static SSR mode forces them into this traditional request/response cycle instead of interactive rendering. -For pages that work with interactive SPA-style rendering, you shouldn't force them to use static SSR rendering, as it's less efficient and less responsive for the end user. +For pages that work with interactive rendering, you shouldn't force them to use static SSR rendering, as it's less efficient and less responsive for the end user. :::moniker-end diff --git a/aspnetcore/release-notes/aspnetcore-9/includes/blazor.md b/aspnetcore/release-notes/aspnetcore-9/includes/blazor.md index e4bc25ef5d54..a93a7c7c8ecc 100644 --- a/aspnetcore/release-notes/aspnetcore-9/includes/blazor.md +++ b/aspnetcore/release-notes/aspnetcore-9/includes/blazor.md @@ -1,5 +1,3 @@ - ### Constructor injection From d293d296a5a5ec8f08a9d6f8195f6c85a9b8b94f Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Mon, 20 May 2024 08:12:35 -0400 Subject: [PATCH 3/3] Updates --- aspnetcore/blazor/components/render-modes.md | 17 ++++++++------- .../aspnetcore-9/includes/blazor.md | 21 +++++++++++-------- 2 files changed, 22 insertions(+), 16 deletions(-) diff --git a/aspnetcore/blazor/components/render-modes.md b/aspnetcore/blazor/components/render-modes.md index eada8a1f3314..4e8d9221870f 100644 --- a/aspnetcore/blazor/components/render-modes.md +++ b/aspnetcore/blazor/components/render-modes.md @@ -517,6 +517,8 @@ The following component results in a runtime error when the component is rendere There are cases where the app's specification calls for components to adopt static server-side rendering (static SSR) and only run on the server, while the rest of the app uses an interactive render mode. +This approach is only useful when the app has specific pages that can't work with interactive Server or WebAssembly rendering. For example, adopt this approach for pages that depend on reading/writing HTTP cookies and can only work in a request/response cycle instead of interactive rendering. For pages that work with interactive rendering, you shouldn't force them to use static SSR rendering, as it's less efficient and less responsive for the end user. + :::moniker range=">= aspnetcore-9.0" Mark any Razor component page with the `[ExcludeFromInteractiveRouting]` attribute assigned with the `@attribute` Razor directive: @@ -525,9 +527,14 @@ Mark any Razor component page with the `[ExcludeFromInteractiveRouting]` attribu @attribute [ExcludeFromInteractiveRouting] ``` -This approach is only useful when you have specific pages that can't work with interactive Server or WebAssembly rendering. For example, adopt this approach for pages that include code that depends on reading/writing HTTP cookies and can only work in a request/response cycle instead of interactive rendering. +Applying the attribute causes navigation to the page to exit from interactive routing. That is, inbound navigation is forced to perform a full-page reload instead resolving the page via interactive routing. The full-page reload forces the top-level root component, typically the `App` component (`App.razor`), to rerender from the server, allowing the app to switch to a different top-level render mode. + +The `HttpContext.AcceptsInteractiveRouting` extension method allows the component to detect whether `[ExcludeFromInteractiveRouting]` is applied to the current page. + +In the `App` component, use the pattern in the following example: -In the `App` component, use the following pattern, where all pages default to the `InteractiveServer` render mode, retaining global interactivity, except for pages annotated with `[ExcludeFromInteractiveRouting]`, which only render with static SSR. You can replace `InteractiveServer` with `InteractiveWebAssembly` or `InteractiveAuto` to specify a different default global render mode. +* Pages that aren't annotated with `[ExcludeFromInteractiveRouting]` default to the `InteractiveServer` render mode with global interactivity. You can replace `InteractiveServer` with `InteractiveWebAssembly` or `InteractiveAuto` to specify a different default global render mode. +* Pages annotated with `[ExcludeFromInteractiveRouting]` adopt static SSR (`PageRenderMode` is `null`). ```razor @@ -551,11 +558,7 @@ In the `App` component, use the following pattern, where all pages default to th } ``` -The `HttpContext.AcceptsInteractiveRouting` extension method allows the component to detect whether `[ExcludeFromInteractiveRouting]` is applied to the current page. Alternatively, you can read endpoint metadata manually using `HttpContext.GetEndpoint()?.Metadata`. - -This approach is useful only if you have specific pages that can't work with interactive Server or WebAssembly rendering. For example, adopt this approach for pages that include code that depends on reading/writing HTTP cookies and can only work in a request/response cycle. Forcing those pages to use static SSR mode forces them into this traditional request/response cycle instead of interactive rendering. - -For pages that work with interactive rendering, you shouldn't force them to use static SSR rendering, as it's less efficient and less responsive for the end user. +An alternative to using the `HttpContext.AcceptsInteractiveRouting` extension method is to read endpoint metadata manually using `HttpContext.GetEndpoint()?.Metadata`. :::moniker-end diff --git a/aspnetcore/release-notes/aspnetcore-9/includes/blazor.md b/aspnetcore/release-notes/aspnetcore-9/includes/blazor.md index a93a7c7c8ecc..005b27d10dee 100644 --- a/aspnetcore/release-notes/aspnetcore-9/includes/blazor.md +++ b/aspnetcore/release-notes/aspnetcore-9/includes/blazor.md @@ -1,16 +1,23 @@ ### Add static server-side rendering (SSR) pages to a globally-interactive Blazor Web App -The Blazor Web App template includes an option to enable *global interactivity*, which means that all pages run on Server, WebAssembly, or Auto (Server and WebAssembly) interactivity mode. Until the release of .NET 9, it wasn't possible to add static SSR pages to apps that adopt global interactivity. +With the release of .NET 9, it's now simpler to add static SSR pages to apps that adopt global interactivity. -Now, you can mark any Razor component page with the new `[ExcludeFromInteractiveRouting]` attribute assigned with the `@attribute` Razor directive: +This approach is only useful when the app has specific pages that can't work with interactive Server or WebAssembly rendering. For example, adopt this approach for pages that depend on reading/writing HTTP cookies and can only work in a request/response cycle instead of interactive rendering. For pages that work with interactive rendering, you shouldn't force them to use static SSR rendering, as it's less efficient and less responsive for the end user. + +Mark any Razor component page with the new `[ExcludeFromInteractiveRouting]` attribute assigned with the `@attribute` Razor directive: ```razor @attribute [ExcludeFromInteractiveRouting] ``` -Applying the attribute causes navigation to the page to exit from interactive routing. That is, inbound navigation is forced to perform a full-page reload instead resolving the page via interactive routing. The full-page reload forces the top-level root component, typically the `App` component (`App.razor`), to rerender from the server, allowing you to switch to a different top-level render mode. +Applying the attribute causes navigation to the page to exit from interactive routing. That is, inbound navigation is forced to perform a full-page reload instead resolving the page via interactive routing. The full-page reload forces the top-level root component, typically the `App` component (`App.razor`), to rerender from the server, allowing the to switch to a different top-level render mode. + +The `HttpContext.AcceptsInteractiveRouting` extension method allows the component to detect whether `[ExcludeFromInteractiveRouting]` is applied to the current page. + +In the `App` component, use the pattern in the following example: -In the `App` component, use the following pattern, where all pages default to the `InteractiveServer` render mode, retaining global interactivity, except for pages annotated with `[ExcludeFromInteractiveRouting]`, which only render with static SSR. You can replace `InteractiveServer` with `InteractiveWebAssembly` or `InteractiveAuto` to specify a different default global render mode. +* Pages that aren't annotated with `[ExcludeFromInteractiveRouting]` default to the `InteractiveServer` render mode with global interactivity. You can replace `InteractiveServer` with `InteractiveWebAssembly` or `InteractiveAuto` to specify a different default global render mode. +* Pages annotated with `[ExcludeFromInteractiveRouting]` adopt static SSR (`PageRenderMode` is `null`). ```razor @@ -34,11 +41,7 @@ In the `App` component, use the following pattern, where all pages default to th } ``` -The new `HttpContext.AcceptsInteractiveRouting` extension method is a helper that makes it easy to detect whether `[ExcludeFromInteractiveRouting]` is applied to the current page. Alternatively, you can read endpoint metadata manually using `HttpContext.GetEndpoint()?.Metadata`. - -This approach is only useful when you have specific pages that can't work with interactive Server or WebAssembly rendering. For example, adopt this approach for pages that include code that depends on reading/writing HTTP cookies and can only work in a request/response cycle instead of interactive rendering. - -For pages that work with interactive rendering, you shouldn't force them to use static SSR rendering, as it's less efficient and less responsive for the end user. +An alternative to using the `HttpContext.AcceptsInteractiveRouting` extension method is to read endpoint metadata manually using `HttpContext.GetEndpoint()?.Metadata`. This feature is covered by the reference documentation in .