From bc856611d8690541d0c1157fc8f523f9fc418171 Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Wed, 2 Jul 2025 13:09:36 -0400 Subject: [PATCH 01/22] [Pre6] Blazor Preview 6 updates --- 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 86edd1ea202f..2267f332d4cf 100644 --- a/aspnetcore/release-notes/aspnetcore-10/includes/blazor.md +++ b/aspnetcore/release-notes/aspnetcore-10/includes/blazor.md @@ -381,8 +381,8 @@ To revert to the previous behavior of throwing a Date: Wed, 2 Jul 2025 13:38:20 -0400 Subject: [PATCH 02/22] Blazor diagnostics improvements --- aspnetcore/blazor/performance/index.md | 33 +++++++++++--------------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/aspnetcore/blazor/performance/index.md b/aspnetcore/blazor/performance/index.md index 20266345d2d2..5bba7b2665dd 100644 --- a/aspnetcore/blazor/performance/index.md +++ b/aspnetcore/blazor/performance/index.md @@ -46,6 +46,7 @@ builder.Services.ConfigureOpenTelemetryMeterProvider(meterProvider => builder.Services.ConfigureOpenTelemetryTracerProvider(tracerProvider => { tracerProvider.AddSource("Microsoft.AspNetCore.Components"); + tracerProvider.AddSource("Microsoft.AspNetCore.Components.Server.Circuits"); }); ``` @@ -81,34 +82,28 @@ Circuit lifecycle tracing: `Microsoft.AspNetCore.Components.CircuitStart`: Traces circuit initialization with the format `Circuit {circuitId}`. -* Tag: `aspnetcore.components.circuit.id` -* Link: HTTP activity +Tag: `aspnetcore.components.circuit.id` Navigation tracing: `Microsoft.AspNetCore.Components.RouteChange`: Tracks route changes with the format `Route {route} -> {componentType}`. -* Tags - * `aspnetcore.components.circuit.id` - * `aspnetcore.components.route` - * `aspnetcore.components.type` -* Links - * HTTP trace - * Circuit trace +Tags: + +* `aspnetcore.components.circuit.id` +* `aspnetcore.components.route` +* `aspnetcore.components.type` Event handling tracing: `Microsoft.AspNetCore.Components.HandleEvent`: Traces event handling with the format `Event {attributeName} -> {componentType}.{methodName}`. -* Tags - * `aspnetcore.components.attribute.name` - * `aspnetcore.components.circuit.id` - * `aspnetcore.components.method` - * `aspnetcore.components.type` - * `error.type` -* Links - * HTTP trace - * Circuit trace - * Router trace +Tags: + +* `aspnetcore.components.attribute.name` +* `aspnetcore.components.circuit.id` +* `aspnetcore.components.method` +* `aspnetcore.components.type` +* `error.type` :::moniker-end From b2b780593d9bcdff58aa2343318015d7f6d6d1f7 Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Wed, 2 Jul 2025 15:28:58 -0400 Subject: [PATCH 03/22] JavaScript bundler support --- aspnetcore/blazor/host-and-deploy/index.md | 26 +++++++++++++++++++ .../webassembly/azure-static-web-apps.md | 2 +- ...bly-browser-developer-tools-diagnostics.md | 2 +- .../webassembly-event-pipe-diagnostics.md | 2 +- .../blazor/webassembly-native-dependencies.md | 2 +- aspnetcore/client-side/using-grunt.md | 12 ++++----- .../aspnetcore-10/includes/blazor.md | 6 +++++ aspnetcore/security/app-secrets.md | 2 +- 8 files changed, 43 insertions(+), 11 deletions(-) diff --git a/aspnetcore/blazor/host-and-deploy/index.md b/aspnetcore/blazor/host-and-deploy/index.md index f2e6655cdb02..61d143af6ea2 100644 --- a/aspnetcore/blazor/host-and-deploy/index.md +++ b/aspnetcore/blazor/host-and-deploy/index.md @@ -84,6 +84,32 @@ For more information on *solutions*, see true +``` + +> [!IMPORTANT] +> This feature only produces the bundler-friendly output when publishing the app. + +The output isn't directly runnable in the browser, but it can be consumed by JavaScript tools to bundle JavaScript files with the rest of the developer-supplied scripts. + +The [`package.json`](https://github.com/dotnet/runtime/blob/main/src/mono/wasm/testassets/JavascriptBundlers/package.json) and [`rollup.config.mjs`](https://github.com/dotnet/runtime/blob/main/src/mono/wasm/testassets/JavascriptBundlers/rollup.config.mjs) files in the `dotnet/runtime` GitHub repository contain a demonstration of bundler-friendly output for an npm project with [Rollup](https://rollupjs.org/). Rollup can compile all of the app's JavaScript files into a single file and handle additional .NET files by copying them to the npm build output. + +> [!NOTE] +> Replacing the `files` plugin with `url`, all of the app's JavaScript files, including the Blazor-WebAssembly runtime (base64 encoded in the JavaScript), are bundled into the output. The size of the file is significantly larger (for example, 300% larger) than when the files are curated with the `files` plugin, so we don't recommend using the `url` plugin as a general practice when producing bundler-friendly output for JavaScript bundler processing. + +For a demonstration sample app, see the [.NET on WASM in a React component sample (`maraf/dotnet-wasm-react` GitHub repository)](https://github.com/maraf/dotnet-wasm-react). + +:::moniker-end + ## Blazor Server `MapFallbackToPage` configuration *This section only applies to Blazor Server apps. isn't supported in Blazor Web Apps and Blazor WebAssembly apps.* diff --git a/aspnetcore/blazor/host-and-deploy/webassembly/azure-static-web-apps.md b/aspnetcore/blazor/host-and-deploy/webassembly/azure-static-web-apps.md index 6fb9d181e2d6..4e9fb0156e1b 100644 --- a/aspnetcore/blazor/host-and-deploy/webassembly/azure-static-web-apps.md +++ b/aspnetcore/blazor/host-and-deploy/webassembly/azure-static-web-apps.md @@ -40,7 +40,7 @@ To deploy from Visual Studio, create a publish profile for Azure Static Web Apps 1. In the publish profile configuration, provide the **Subscription name**. Select an existing instance, or select **Create a new instance**. When creating a new instance in the Azure portal's **Create Static Web App** UI, set the **Deployment details** > **Source** to **Other**. Wait for the deployment to complete in the Azure portal before proceeding. -1. In the publish profile configuration, select the Azure Static Web Apps instance from the instance's resource group. Select **Finish** to create the publish profile. If Visual Studio prompts to install the Static Web Apps (SWA) CLI, install the CLI by following the prompts. The SWA CLI requires [NPM/Node.js (Visual Studio documentation)](/visualstudio/javascript/npm-package-management). +1. In the publish profile configuration, select the Azure Static Web Apps instance from the instance's resource group. Select **Finish** to create the publish profile. If Visual Studio prompts to install the Static Web Apps (SWA) CLI, install the CLI by following the prompts. The SWA CLI requires [npm/Node.js (Visual Studio documentation)](/visualstudio/javascript/npm-package-management). After the publish profile is created, deploy the app to the Azure Static Web Apps instance using the publish profile by selecting the **Publish** button. diff --git a/aspnetcore/blazor/performance/webassembly-browser-developer-tools-diagnostics.md b/aspnetcore/blazor/performance/webassembly-browser-developer-tools-diagnostics.md index ccf9df2c5b73..24f41e29f3ce 100644 --- a/aspnetcore/blazor/performance/webassembly-browser-developer-tools-diagnostics.md +++ b/aspnetcore/blazor/performance/webassembly-browser-developer-tools-diagnostics.md @@ -57,7 +57,7 @@ In the app's project file (`.csproj`): ``` -Alternatively, enable features when the app is built with the .NET CLI. The following options passed to the `dotnet build` command mirror the preceding MS Build property configuration: +Alternatively, enable features when the app is built with the .NET CLI. The following options passed to the `dotnet build` command mirror the preceding MSBuild property configuration: ```dotnetcli /p:WasmProfilers=browser /p:WasmNativeStrip=false /p:WasmNativeDebugSymbols=true diff --git a/aspnetcore/blazor/performance/webassembly-event-pipe-diagnostics.md b/aspnetcore/blazor/performance/webassembly-event-pipe-diagnostics.md index 6c9e653c48a4..b286971d54d5 100644 --- a/aspnetcore/blazor/performance/webassembly-event-pipe-diagnostics.md +++ b/aspnetcore/blazor/performance/webassembly-event-pipe-diagnostics.md @@ -71,7 +71,7 @@ In the app's project file (`.csproj`): ``` -Alternatively, enable features when the app is built with the .NET CLI. The following options passed to the `dotnet build` command mirror the preceding MS Build property configuration: +Alternatively, enable features when the app is built with the .NET CLI. The following options passed to the `dotnet build` command mirror the preceding MSBuild property configuration: ```dotnetcli /p:WasmPerfTracing=true /p:WasmPerfInstrumentation=all /p:MetricsSupport=true /p:EventSourceSupport=true diff --git a/aspnetcore/blazor/webassembly-native-dependencies.md b/aspnetcore/blazor/webassembly-native-dependencies.md index 6d0067df59c6..aed9bcec776b 100644 --- a/aspnetcore/blazor/webassembly-native-dependencies.md +++ b/aspnetcore/blazor/webassembly-native-dependencies.md @@ -52,7 +52,7 @@ int fact(int n) } ``` -Add a `NativeFileReference` MS Build item for `Test.c` in the app's project file (`.csproj`): +Add a `NativeFileReference` MSBuild item for `Test.c` in the app's project file (`.csproj`): ```xml diff --git a/aspnetcore/client-side/using-grunt.md b/aspnetcore/client-side/using-grunt.md index 2cd81b90d003..41fdfde4714e 100644 --- a/aspnetcore/client-side/using-grunt.md +++ b/aspnetcore/client-side/using-grunt.md @@ -77,18 +77,18 @@ To begin, set up a new empty web application and add TypeScript example files. T } ``` -## Configuring NPM +## Configuring npm -Next, configure NPM to download grunt and grunt-tasks. +Next, configure npm to download grunt and grunt-tasks. -1. In the Solution Explorer, right-click the project and select **Add > New Item** from the context menu. Select the **NPM configuration file** item, leave the default name, `package.json`, and click the **Add** button. +1. In the Solution Explorer, right-click the project and select **Add > New Item** from the context menu. Select the **npm Configuration File** item, leave the default name, `package.json`, and click the **Add** button. 2. In the `package.json` file, inside the `devDependencies` object braces, enter "grunt". Select `grunt` from the Intellisense list and press the Enter key. Visual Studio will quote the grunt package name, and add a colon. To the right of the colon, select the latest stable version of the package from the top of the Intellisense list (press `Ctrl-Space` if Intellisense doesn't appear). ![grunt Intellisense](using-grunt/_static/devdependencies-grunt.png) > [!NOTE] - > NPM uses [semantic versioning](https://semver.org/) to organize dependencies. Semantic versioning, also known as SemVer, identifies packages with the numbering scheme \.\.\. Intellisense simplifies semantic versioning by showing only a few common choices. The top item in the Intellisense list (0.4.5 in the example above) is considered the latest stable version of the package. The caret (^) symbol matches the most recent major version and the tilde (~) matches the most recent minor version. See the [NPM semver version parser reference](https://www.npmjs.com/package/semver) as a guide to the full expressivity that SemVer provides. + > npm uses [semantic versioning](https://semver.org/) to organize dependencies. Semantic versioning, also known as SemVer, identifies packages with the numbering scheme \.\.\. Intellisense simplifies semantic versioning by showing only a few common choices. The top item in the Intellisense list (0.4.5 in the example above) is considered the latest stable version of the package. The caret (^) symbol matches the most recent major version and the tilde (~) matches the most recent minor version. See the [npm semver version parser reference](https://www.npmjs.com/package/semver) as a guide to the full expressivity that SemVer provides. 3. Add more dependencies to load grunt-contrib-\* packages for *clean*, *jshint*, *concat*, *uglify*, and *watch* as shown in the example below. The versions don't need to match the example. @@ -110,7 +110,7 @@ The packages for each `devDependencies` item will download, along with any files ![grunt node_modules](using-grunt/_static/node-modules.png) > [!NOTE] -> If you need to, you can manually restore dependencies in **Solution Explorer** by right-clicking on `Dependencies\NPM` and selecting the **Restore Packages** menu option. +> If you need to, you can manually restore dependencies in **Solution Explorer** by right-clicking on `Dependencies\npm` and selecting the **Restore Packages** menu option. ![restore packages](using-grunt/_static/restore-packages.png) @@ -277,4 +277,4 @@ Unload and reload the project. When the project loads again, the watch task star ## Summary -Grunt is a powerful task runner that can be used to automate most client-build tasks. Grunt leverages NPM to deliver its packages, and features tooling integration with Visual Studio. Visual Studio's Task Runner Explorer detects changes to configuration files and provides a convenient interface to run tasks, view running tasks, and bind tasks to Visual Studio events. +Grunt is a powerful task runner that can be used to automate most client-build tasks. Grunt leverages npm to deliver its packages, and features tooling integration with Visual Studio. Visual Studio's Task Runner Explorer detects changes to configuration files and provides a convenient interface to run tasks, view running tasks, and bind tasks to Visual Studio events. diff --git a/aspnetcore/release-notes/aspnetcore-10/includes/blazor.md b/aspnetcore/release-notes/aspnetcore-10/includes/blazor.md index 2267f332d4cf..9d81cb4384fe 100644 --- a/aspnetcore/release-notes/aspnetcore-10/includes/blazor.md +++ b/aspnetcore/release-notes/aspnetcore-10/includes/blazor.md @@ -422,3 +422,9 @@ For more information, see . + +### JavaScript bundler support + +Blazor's build output isn't compatible with JavaScript bundlers, such as [Gulp](https://gulpjs.com), [Webpack](https://webpack.js.org), and [Rollup](https://rollupjs.org/). Blazor can now produce bundler-friendly output during publish by setting the `WasmBundlerFriendlyBootConfig` MSBuild property to `true`. + +For more information, see . diff --git a/aspnetcore/security/app-secrets.md b/aspnetcore/security/app-secrets.md index e2b5bc3b3e86..87361e78a89d 100644 --- a/aspnetcore/security/app-secrets.md +++ b/aspnetcore/security/app-secrets.md @@ -80,7 +80,7 @@ dotnet user-secrets init The preceding command adds a `UserSecretsId` element within a `PropertyGroup` of the project file. By default, the inner text of `UserSecretsId` is a GUID. The inner text is arbitrary, but is unique to the project. -![The UserSecretsId MS Build property configuration in the app's project file.](~/security/app-secrets/_static/UserSecretsId.png) +![The UserSecretsId MSBuild property configuration in the app's project file.](~/security/app-secrets/_static/UserSecretsId.png) ### Use Visual Studio From 0a3c8ae090c29463a39e6bc2959d3876d626a41a Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Wed, 2 Jul 2025 15:56:32 -0400 Subject: [PATCH 04/22] Blazor WebAssembly static asset preloading --- .../blazor/host-and-deploy/server/index.md | 17 +++++++++++++++++ .../aspnetcore-10/includes/blazor.md | 17 ++++++++++++++++- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/aspnetcore/blazor/host-and-deploy/server/index.md b/aspnetcore/blazor/host-and-deploy/server/index.md index e39724e52efc..0ddf4f14d127 100644 --- a/aspnetcore/blazor/host-and-deploy/server/index.md +++ b/aspnetcore/blazor/host-and-deploy/server/index.md @@ -40,6 +40,23 @@ For guidance on building secure and scalable server-side Blazor apps, see the fo Each circuit uses approximately 250 KB of memory for a minimal *Hello World*-style app. The size of a circuit depends on the app's code and the state maintenance requirements associated with each component. We recommend that you measure resource demands during development for your app and infrastructure, but the following baseline can be a starting point in planning your deployment target: If you expect your app to support 5,000 concurrent users, consider budgeting at least 1.3 GB of server memory to the app (or ~273 KB per user). +:::moniker range=">= aspnetcore-10.0" + +## Blazor WebAssembly static asset preloading + +The `LinkPreload` component in the `App` component (`App.razor`) is used to reference Blazor static assets. The component is placed after the `` tag: + +```razor + +``` + +A Razor component is used instead of `` elements because: + +* The component permits the `` tag 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. + +:::moniker-end + ## SignalR configuration [SignalR's hosting and scaling conditions](xref:signalr/publish-to-azure-web-app) apply to Blazor apps that use SignalR. diff --git a/aspnetcore/release-notes/aspnetcore-10/includes/blazor.md b/aspnetcore/release-notes/aspnetcore-10/includes/blazor.md index 9d81cb4384fe..f6843515a4f0 100644 --- a/aspnetcore/release-notes/aspnetcore-10/includes/blazor.md +++ b/aspnetcore/release-notes/aspnetcore-10/includes/blazor.md @@ -427,4 +427,19 @@ For more information, see . +For more information, see . + +### Blazor WebAssembly static asset preloading in Blazor Web Apps + +We replaced `` 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. + +Removing the component disables the feature if the app is using a [`loadBootResource` callback](xref:blazor/fundamentals/startup#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 `` tag in the `App` component (`App.razor`): + +```diff + ++ +``` + +For more information, see . From 34126b4f6b0ac110d740d892b8a29c0b2cf25b6d Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Wed, 2 Jul 2025 20:45:25 -0400 Subject: [PATCH 05/22] Updates --- aspnetcore/blazor/host-and-deploy/server/index.md | 4 ++-- .../release-notes/aspnetcore-10/includes/blazor.md | 10 +++++++--- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/aspnetcore/blazor/host-and-deploy/server/index.md b/aspnetcore/blazor/host-and-deploy/server/index.md index 0ddf4f14d127..70135ea17c6b 100644 --- a/aspnetcore/blazor/host-and-deploy/server/index.md +++ b/aspnetcore/blazor/host-and-deploy/server/index.md @@ -44,7 +44,7 @@ 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 (`App.razor`) is used to reference Blazor static assets. The component is placed after the `` tag: +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 (``): ```razor @@ -52,7 +52,7 @@ The `LinkPreload` component in the `App` component (`App.razor`) is used to refe A Razor component is used instead of `` elements because: -* The component permits the `` tag to correctly identify the root of the Blazor app within an ASP.NET Core app. +* 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. :::moniker-end diff --git a/aspnetcore/release-notes/aspnetcore-10/includes/blazor.md b/aspnetcore/release-notes/aspnetcore-10/includes/blazor.md index f6843515a4f0..891469ad2ba7 100644 --- a/aspnetcore/release-notes/aspnetcore-10/includes/blazor.md +++ b/aspnetcore/release-notes/aspnetcore-10/includes/blazor.md @@ -435,11 +435,15 @@ We replaced `` headers with a `LinkPreload` component (``) Removing the component disables the feature if the app is using a [`loadBootResource` callback](xref:blazor/fundamentals/startup#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 `` tag in the `App` component (`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 `LinkPreload` component after the base URL tag (``) in the `App` component's head content (`App.razor`): ```diff - -+ + + ... + ++ + ... + ``` For more information, see . From aa888812e727b63951808fc7194044c9e0989b23 Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Thu, 3 Jul 2025 09:09:26 -0400 Subject: [PATCH 06/22] Streaming Rendering and Not Found behavior --- aspnetcore/blazor/components/rendering.md | 10 ++++++++++ aspnetcore/blazor/fundamentals/routing.md | 4 +--- .../release-notes/aspnetcore-10/includes/blazor.md | 6 ++---- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/aspnetcore/blazor/components/rendering.md b/aspnetcore/blazor/components/rendering.md index 756d63ca619d..456e96859c70 100644 --- a/aspnetcore/blazor/components/rendering.md +++ b/aspnetcore/blazor/components/rendering.md @@ -53,6 +53,16 @@ Streaming rendering requires the server to avoid buffering the output. The respo To stream content updates when using static server-side rendering (static SSR) or prerendering, apply the [`[StreamRendering]` attribute](xref:Microsoft.AspNetCore.Components.StreamRenderingAttribute) in .NET 9 or later (use `[StreamRendering(true)]` in .NET 8) to the component. Streaming rendering must be explicitly enabled because streamed updates may cause content on the page to shift. Components without the attribute automatically adopt streaming rendering if the parent component uses the feature. Pass `false` to the attribute in a child component to disable the feature at that point and further down the component subtree. The attribute is functional when applied to components supplied by a [Razor class library](xref:blazor/components/class-libraries). +:::moniker-end + +:::moniker range=">= aspnetcore-10.0" + +If [enhanced navigation](xref:blazor/fundamentals/routing#enhanced-navigation-and-form-handling) is active, streaming rendering renders [Not Found responses](xref:blazor/fundamentals/routing#not-found-responses) without reloading the page. When enhanced navigation is blocked, the framework redirects to Not Found content with a page refresh. In these contexts, Not Found content is defined as a [`NotFoundPage` passed to the `Router` component](xref:blazor/fundamentals/routing#not-found-responses), otherwise as a re-execution page when [re-execution middleware is set](xref:fundamentals/error-handling#usestatuscodepageswithreexecute) with . + +:::moniker-end + +:::moniker range=">= aspnetcore-8.0" + The following example is based on the `Weather` component in an app created from the [Blazor Web App project template](xref:blazor/project-structure#blazor-web-app). The call to simulates retrieving weather data asynchronously. The component initially renders placeholder content ("`Loading...`") without waiting for the asynchronous delay to complete. When the asynchronous delay completes and the weather data content is generated, the content is streamed to the response and patched into the weather forecast table. `Weather.razor`: diff --git a/aspnetcore/blazor/fundamentals/routing.md b/aspnetcore/blazor/fundamentals/routing.md index 391bd075112c..b21cade0880c 100644 --- a/aspnetcore/blazor/fundamentals/routing.md +++ b/aspnetcore/blazor/fundamentals/routing.md @@ -708,13 +708,11 @@ For more information on component disposal, see -*In .NET 10 Preview 4, Not Found responses are only available for static SSR and global interactive rendering. Per-page/component rendering support is planned for Preview 5 in June, 2025.* - provides a `NotFound` method to handle scenarios where a requested resource isn't found during static server-side rendering (static SSR) or global interactive rendering: * **Static SSR**: Calling `NotFound` sets the HTTP status code to 404. -* **Streaming rendering**: Throws an exception if the response has already started. * **Interactive rendering**: Signals the Blazor router ([`Router` component](xref:blazor/fundamentals/routing#route-templates)) to render Not Found content. +* **Streaming rendering**: If [enhanced navigation](xref:blazor/fundamentals/routing#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. In these contexts, Not Found content is defined as a `NotFoundPage` passed to the `Router` component (described later in this section), otherwise as a re-execution page when [re-execution middleware is set](xref:fundamentals/error-handling#usestatuscodepageswithreexecute) with . When a component is rendered statically (static SSR) and `NavigationManager.NotFound` is called, the 404 status code is set on the response: diff --git a/aspnetcore/release-notes/aspnetcore-10/includes/blazor.md b/aspnetcore/release-notes/aspnetcore-10/includes/blazor.md index 891469ad2ba7..8e93fcbb8abb 100644 --- a/aspnetcore/release-notes/aspnetcore-10/includes/blazor.md +++ b/aspnetcore/release-notes/aspnetcore-10/includes/blazor.md @@ -390,10 +390,8 @@ AppContext.SetSwitch( The now includes a `NotFound` method to handle scenarios where a requested resource isn't found during static server-side rendering (static SSR) or global interactive rendering: * **Static server-side rendering (static SSR)**: Calling `NotFound` sets the HTTP status code to 404. -* **Streaming rendering**: Throws an exception if the response has already started. * **Interactive rendering**: Signals the Blazor router ([`Router` component](xref:blazor/fundamentals/routing?view=aspnetcore-10.0#route-templates)) to render Not Found content. - -Per-page/component rendering support is planned for Preview 5 in June, 2025. +* **Streaming rendering**: If [enhanced navigation](xref:blazor/fundamentals/routing#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. In these contexts, Not Found content is defined as a `NotFoundPage` passed to the `Router` component (described in the next section of this article), otherwise as a re-execution page when [re-execution middleware is set](xref:fundamentals/error-handling#usestatuscodepageswithreexecute) with . You can use the `NavigationManager.OnNotFound` event for notifications when `NotFound` is invoked. @@ -401,7 +399,7 @@ For more information and examples, see From 5b8cfe5410541ed7938bfb128548084d26a8bd7e Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Thu, 3 Jul 2025 09:55:51 -0400 Subject: [PATCH 07/22] Improved form validation (nested models and collection types) --- aspnetcore/blazor/forms/validation.md | 98 ++++++++++++++++++- .../aspnetcore-10/includes/blazor.md | 77 +++++++++++++++ 2 files changed, 174 insertions(+), 1 deletion(-) diff --git a/aspnetcore/blazor/forms/validation.md b/aspnetcore/blazor/forms/validation.md index d7afa0977288..7ae5ee4ec073 100644 --- a/aspnetcore/blazor/forms/validation.md +++ b/aspnetcore/blazor/forms/validation.md @@ -1557,12 +1557,108 @@ The doesn't work w :::moniker-end -## Nested models, collection types, and complex types +:::moniker range=">= aspnetcore-10.0" + +## Nested objects and collection types + +Blazor form validation includes support for validating properties of nested objects and collection items with the built-in . + +To create a validated form, use a component inside an component, just as before. + +To opt into the nested objects and collection types validation feature: + + + +1. Call the `AddValidation` extension method in the `Program` file where services are registered. +2. Declare the form model types in a C# class file, not in a Razor component (`.razor`). +3. Annotate the root form model type with the `[ValidatableType]` attribute. + +Without following the preceding steps, form validation behavior doesn't include nested model and collection type validation. + + + +The following example demonstrates customer orders with the improved form validation (details omitted for brevity): + +In `Program.cs`, call `AddValidation` on the service collection: + +```csharp +builder.Services.AddValidation(); +``` + +In the following `Order` class, the `[ValidatableType]` attribute is required on the top-level model type. The other types are discovered automatically. `OrderItem` and `ShippingAddress` aren't shown for brevity, but nested and collection validation works the same way in those types if they were shown. + +`Order.cs`: + +```csharp +[ValidatableType] +public class Order +{ + public Customer Customer { get; set; } = new(); + public List OrderItems { get; set; } = []; +} + +public class Customer +{ + [Required(ErrorMessage = "Name is required.")] + public string? FullName { get; set; } + + [Required(ErrorMessage = "Email is required.")] + public string? Email { get; set; } + + public ShippingAddress ShippingAddress { get; set; } = new(); +} +``` + +In the following `OrderPage` component, the component is present in the component. + +`OrderPage.razor`: + +```csharp + + + +

Customer Details

+
+ + +
+ + // ... form continues ... +
+ +@code { + public Order? Model { get; set; } + + protected override void OnInitialized() => Model ??= new(); +} +``` + +The requirement to declare the model types outside of Razor components (`.razor` files) is due to the fact that both the new validation feature and the Razor compiler itself are using a source generator. Currently, output of one source generator can't be used as an input for another source generator. + +## Complex types + +Blazor provides support for validating form input using data annotations with the built-in . However, the only validates top-level properties of the model bound to the form that aren't complex-type properties. + +To validate the bound model's entire object graph, including complex-type properties, use the `ObjectGraphDataAnnotationsValidator` provided by the *experimental* [`Microsoft.AspNetCore.Components.DataAnnotations.Validation`](https://www.nuget.org/packages/Microsoft.AspNetCore.Components.DataAnnotations.Validation) package. + +> [!NOTE] +> The `ObjectGraphDataAnnotationsValidator` isn't compatible with [nested objects and collection types validation](#nested-objects-and-collection-types), but it's capable of validating nested objects and collection types on its own. + +:::moniker-end + +:::moniker range="< aspnetcore-10.0" + +## Nested objects, collection types, and complex types Blazor provides support for validating form input using data annotations with the built-in . However, the only validates top-level properties of the model bound to the form that aren't collection- or complex-type properties. To validate the bound model's entire object graph, including collection- and complex-type properties, use the `ObjectGraphDataAnnotationsValidator` provided by the *experimental* [`Microsoft.AspNetCore.Components.DataAnnotations.Validation`](https://www.nuget.org/packages/Microsoft.AspNetCore.Components.DataAnnotations.Validation) package: +:::moniker-end + ```razor diff --git a/aspnetcore/release-notes/aspnetcore-10/includes/blazor.md b/aspnetcore/release-notes/aspnetcore-10/includes/blazor.md index 8e93fcbb8abb..980031bc1694 100644 --- a/aspnetcore/release-notes/aspnetcore-10/includes/blazor.md +++ b/aspnetcore/release-notes/aspnetcore-10/includes/blazor.md @@ -445,3 +445,80 @@ The Blazor Web App template adopts the feature by default in .NET 10, and apps u ``` For more information, see . + +### Improved form validation + +Blazor now has improved form validation capabilities, including support for validating properties of nested objects and collection items. + +To create a validated form, use a component inside an component, just as before. + +To opt into the new validation feature: + +1. Call the `AddValidation` extension method in the `Program` file where services are registered. +2. Declare the form model types in a C# class file, not in a Razor component (`.razor`). +3. Annotate the root form model type with the `[ValidatableType]` attribute. + +Without following the preceding steps, the validation behavior remains the same as in previous .NET releases. + +The following example demonstrates customer orders with the improved form validation (details omitted for brevity): + +In `Program.cs`, call `AddValidation` on the service collection to enable the new validation behavior: + +```csharp +builder.Services.AddValidation(); +``` + +In the following `Order` class, the `[ValidatableType]` attribute is required on the top-level model type. The other types are discovered automatically. `OrderItem` and `ShippingAddress` aren't shown for brevity, but nested and collection validation works the same way in those types if they were shown. + +`Order.cs`: + +```csharp +[ValidatableType] +public class Order +{ + public Customer Customer { get; set; } = new(); + public List OrderItems { get; set; } = []; +} + +public class Customer +{ + [Required(ErrorMessage = "Name is required.")] + public string? FullName { get; set; } + + [Required(ErrorMessage = "Email is required.")] + public string? Email { get; set; } + + public ShippingAddress ShippingAddress { get; set; } = new(); +} +``` + +In the following `OrderPage` component, the component is present in the component. + +`OrderPage.razor`: + +```razor + + + +

Customer Details

+
+ + +
+ + @* ... form continues ... *@ +
+ +@code { + public Order? Model { get; set; } + + protected override void OnInitialized() => Model ??= new(); + + // ... code continues ... +} +``` + +The requirement to declare the model types outside of Razor components (`.razor` files) is due to the fact that both the new validation feature and the Razor compiler itself are using a source generator. Currently, output of one source generator can't be used as an input for another source generator. From 7f57cbad3f3160241c45fd10f8a156ac5c87de34 Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Fri, 4 Jul 2025 08:59:08 -0400 Subject: [PATCH 08/22] Blazor custom cache and MSBuild prop removed --- ...le-caching-and-integrity-check-failures.md | 41 +++++++++++++++---- .../webassembly/http-caching-issues.md | 4 ++ .../aspnetcore-10/includes/blazor.md | 10 +++++ 3 files changed, 48 insertions(+), 7 deletions(-) diff --git a/aspnetcore/blazor/host-and-deploy/webassembly/bundle-caching-and-integrity-check-failures.md b/aspnetcore/blazor/host-and-deploy/webassembly/bundle-caching-and-integrity-check-failures.md index f0f99ec579d5..f3820960428c 100644 --- a/aspnetcore/blazor/host-and-deploy/webassembly/bundle-caching-and-integrity-check-failures.md +++ b/aspnetcore/blazor/host-and-deploy/webassembly/bundle-caching-and-integrity-check-failures.md @@ -1,7 +1,7 @@ --- -title: ASP.NET Core Blazor WebAssembly .NET bundle caching and integrity check failures +title: ASP.NET Core Blazor WebAssembly caching and integrity check failures author: guardrex -description: Learn about WebAssembly .NET app and runtime bundle caching and how to resolve integrity check failures in Blazor WebAssembly apps. +description: Learn about asset caching for Blazor WebAssembly and how to resolve integrity check failures in Blazor WebAssembly apps. monikerRange: '>= aspnetcore-3.1' ms.author: wpickett ms.custom: mvc @@ -12,7 +12,7 @@ uid: blazor/host-and-deploy/webassembly/bundle-caching-and-integrity-check-failu [!INCLUDE[](~/includes/not-latest-version.md)] -This article explains how Blazor WebAssembly caches the WebAssembly .NET runtime and app bundle and how to diagnose and resolve integrity failures. +This article explains asset caching for Blazor WebAssembly and how to diagnose and resolve integrity failures. When a Blazor WebAssembly app loads in the browser, the app downloads boot resources from the server: @@ -20,13 +20,30 @@ When a Blazor WebAssembly app loads in the browser, the app downloads boot resou * .NET runtime and assemblies * Locale specific data -Except for Blazor's boot manifest file (`blazor.boot.json`) prior to the release of .NET 10†, WebAssembly .NET runtime and app bundle files are cached on clients. The Blazor boot configuration, inlined into `dotnet.js` in .NET 10 or later, contains a manifest of the files that make up the app that must be downloaded along with a hash of the file's content that's used to detect whether any of the boot resources have changed. Blazor caches downloaded files using the browser [Cache](https://developer.mozilla.org/docs/Web/API/Cache) API. +:::moniker range=">= aspnetcore-10.0" + +The Blazor boot configuration, inlined into `dotnet.js`, contains a fingerprinted manifest of the files that make up the app that must be downloaded along with a hash of each file's content. The app's files are preloaded and cached by the browser. > [!NOTE] -> †If upgrading an app to .NET 10 or later and the app relies on the `blazor.boot.json` manifest file for custom processing, we recommend collecting the information directly from the build. +> †In .NET 10 or later, the `blazor.boot.json` manifest file is no longer present. If upgrading an app that relies on the manifest file for custom processing, we recommend collecting the information directly from the build. + +When Blazor WebAssembly downloads an app's startup files, it instructs the browser to perform integrity checks on the responses. Blazor sends SHA-256 hash values for DLL (`.dll`), WebAssembly (`.wasm`), and other files. The file hashes of cached files are compared to the hashes in the boot configuration. An error is generated by the browser if any downloaded file's integrity check fails. + +For more information, see the following sections of the *Fundamentals: Static files* article: + +* [Preloaded Blazor framework static assets](xref:blazor/fundamentals/static-files#preloaded-blazor-framework-static-assets) +* [Fingerprint client-side static assets in standalone Blazor WebAssembly apps](xref:blazor/fundamentals/static-files#fingerprint-client-side-static-assets-in-standalone-blazor-webassembly-apps) + +:::moniker-end + +:::moniker range="< aspnetcore-10.0" + +Except for Blazor's boot manifest file (`blazor.boot.json`), WebAssembly .NET runtime and app bundle files are cached on clients. The Blazor boot configuration contains a manifest of the files that make up the app that must be downloaded along with a hash of the file's content that's used to detect whether any of the boot resources have changed. Blazor caches downloaded files using the browser [Cache](https://developer.mozilla.org/docs/Web/API/Cache) API. When Blazor WebAssembly downloads an app's startup files, it instructs the browser to perform integrity checks on the responses. Blazor sends SHA-256 hash values for DLL (`.dll`), WebAssembly (`.wasm`), and other files in the Blazor boot configuration, which isn't cached on clients. The file hashes of cached files are compared to the hashes in the Blazor boot configuration. For cached files with a matching hash, Blazor uses the cached files. Otherwise, files are requested from the server. After a file is downloaded, its hash is checked again for integrity validation. An error is generated by the browser if any downloaded file's integrity check fails. +:::moniker-end + Blazor's algorithm for managing file integrity: * Ensures that the app doesn't risk loading an inconsistent set of files, for example if a new deployment is applied to your web server while the user is in the process of downloading the application files. Inconsistent files can result in a malfunctioning app. @@ -145,6 +162,14 @@ In most cases, don't disable integrity checking. Disabling integrity checking do There may be cases where the web server can't be relied upon to return consistent responses, and you have no choice but to temporarily disable integrity checks until the underlying problem is resolved. +:::moniker range=">= aspnetcore-10.0" + +To disable integrity checks, [manually load client-side boot resources](xref:blazor/fundamentals/startup#load-client-side-boot-resources) and avoid passing the `integrity` parameter to the `fetch` call. + +:::moniker-end + +:::moniker range="< aspnetcore-10.0" + To disable integrity checks, add the following to a property group in the Blazor WebAssembly app's project file (`.csproj`): ```xml @@ -153,10 +178,12 @@ To disable integrity checks, add the following to a property group in the Blazor `BlazorCacheBootResources` also disables Blazor's default behavior of caching the `.dll`, `.wasm`, and other files based on their SHA-256 hashes because the property indicates that the SHA-256 hashes can't be relied upon for correctness. Even with this setting, the browser's normal HTTP cache may still cache those files, but whether or not this happens depends on your web server configuration and the `cache-control` headers that it serves. +:::moniker-end + > [!NOTE] -> The `BlazorCacheBootResources` property doesn't disable integrity checks for [Progressive Web Applications (PWAs)](xref:blazor/progressive-web-app). For guidance pertaining to PWAs, see the [Disable integrity checking for PWAs](#disable-integrity-checking-for-pwas) section. +> The preceding approach doesn't disable integrity checks for [Progressive Web Applications (PWAs)](xref:blazor/progressive-web-app). For guidance pertaining to PWAs, see the [Disable integrity checking for PWAs](#disable-integrity-checking-for-pwas) section. -We can't provide an exhaustive list of scenarios where disabling integrity checking is required. Servers can answer a request in arbitrary ways outside of the scope of the Blazor framework. The framework provides the `BlazorCacheBootResources` setting to make the app runnable at the cost of *losing a guarantee of integrity that the app can provide*. Again, we don't recommend disabling integrity checking, especially for production deployments. Developers should seek to solve the underlying integrity problem that's causing integrity checking to fail. +We can't provide an exhaustive list of scenarios where disabling integrity checking is required. Servers can answer a request in arbitrary ways outside of the scope of the Blazor framework. The framework permits the preceding approach to make the app runnable at the cost of *losing a guarantee of integrity that the app can provide*. Again, we don't recommend disabling integrity checking, especially for production deployments. Developers should seek to solve the underlying integrity problem that's causing integrity checking to fail. A few general cases that can cause integrity issues are: diff --git a/aspnetcore/blazor/host-and-deploy/webassembly/http-caching-issues.md b/aspnetcore/blazor/host-and-deploy/webassembly/http-caching-issues.md index e730cddce1d9..7f18a7198b1b 100644 --- a/aspnetcore/blazor/host-and-deploy/webassembly/http-caching-issues.md +++ b/aspnetcore/blazor/host-and-deploy/webassembly/http-caching-issues.md @@ -54,6 +54,8 @@ Usually the source of cache state problems is limited to the HTTP browser cache, You can optionally include the `storage` directive to clear local storage caches at the same time that you're clearing the HTTP browser cache. However, apps that use client storage might experience a loss of important information if the `storage` directive is used. +:::moniker range="< aspnetcore-10.0" + ### Append a query string to the Blazor script tag If none of the previous recommended actions are effective, possible to use for your deployment, or apply to your app, consider temporarily appending a query string to the Blazor script's `