Skip to content

Commit b0cf7bc

Browse files
authored
[Pre6] Blazor Preview 6 updates (#35698)
1 parent 7319ebb commit b0cf7bc

File tree

20 files changed

+618
-73
lines changed

20 files changed

+618
-73
lines changed

aspnetcore/blazor/components/prerender.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ In the following example that serializes state for multiple components of the sa
202202
In the following example that serializes state for a dependency injection service:
203203

204204
* Properties annotated with the `[SupplyParameterFromPersistentComponentState]` attribute are serialized during prerendering and deserialized when the app becomes interactive.
205-
* The `AddPersistentService` 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:
205+
* The <xref:Microsoft.Extensions.DependencyInjection.RazorComponentsRazorComponentBuilderExtensions.RegisterPersistentService%2A> 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:
206206
* `RenderMode.Server`: The service is available for the Interactive Server render mode.
207207
* `RenderMode.Webassembly`: The service is available for the Interactive Webassembly render mode.
208208
* `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.
@@ -231,7 +231,8 @@ public class CounterService
231231
In `Program.cs`:
232232

233233
```csharp
234-
builder.Services.AddPersistentService<CounterService>(RenderMode.InteractiveAuto);
234+
builder.Services.RegisterPersistentService<CounterService>(
235+
RenderMode.InteractiveAuto);
235236
```
236237

237238
Serialized properties are identified from the actual service instance:

aspnetcore/blazor/components/rendering.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,32 @@ Streaming rendering requires the server to avoid buffering the output. The respo
5353

5454
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).
5555

56+
:::moniker-end
57+
58+
:::moniker range=">= aspnetcore-10.0"
59+
60+
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.
61+
62+
Streaming rendering can only render components that have a route, such as a [`NotFoundPage` assignment](xref:blazor/fundamentals/routing#not-found-responses) (`NotFoundPage="..."`) or a [Status Code Pages Re-execution Middleware page assignment](xref:fundamentals/error-handling#usestatuscodepageswithreexecute) (<xref:Microsoft.AspNetCore.Builder.StatusCodePagesExtensions.UseStatusCodePagesWithReExecute%2A>). The Not Found render fragment (`<NotFound>...</NotFound>`) and the `DefaultNotFound` 404 content ("`Not found`" plain text) don't have routes, so they can't be used during streaming rendering.
63+
64+
Streaming `NavigationManager.NotFound` content rendering uses (in order):
65+
66+
* A `NotFoundPage` passed to the `Router` component, if present.
67+
* A Status Code Pages Re-execution Middleware page, if configured.
68+
* No action if neither of the preceding approaches is adopted.
69+
70+
Non-streaming `NavigationManager.NotFound` content rendering uses (in order):
71+
72+
* A `NotFoundPage` passed to the `Router` component, if present.
73+
* Not Found render fragment content, if present. *Not recommended in .NET 10 or later.*
74+
* `DefaultNotFound` 404 content ("`Not found`" plain text).
75+
76+
[Status Code Pages Re-execution Middleware](xref:fundamentals/error-handling#usestatuscodepageswithreexecute) with <xref:Microsoft.AspNetCore.Builder.StatusCodePagesExtensions.UseStatusCodePagesWithReExecute%2A> 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.
77+
78+
:::moniker-end
79+
80+
:::moniker range=">= aspnetcore-8.0"
81+
5682
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 <xref:System.Threading.Tasks.Task.Delay%2A?displayProperty=nameWithType> 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.
5783

5884
`Weather.razor`:

aspnetcore/blazor/forms/validation.md

Lines changed: 97 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1557,12 +1557,108 @@ The <xref:System.ComponentModel.DataAnnotations.CompareAttribute> doesn't work w
15571557

15581558
:::moniker-end
15591559

1560-
## Nested models, collection types, and complex types
1560+
:::moniker range=">= aspnetcore-10.0"
1561+
1562+
## Nested objects and collection types
1563+
1564+
Blazor form validation includes support for validating properties of nested objects and collection items with the built-in <xref:Microsoft.AspNetCore.Components.Forms.DataAnnotationsValidator>.
1565+
1566+
To create a validated form, use a <xref:Microsoft.AspNetCore.Components.Forms.DataAnnotationsValidator> component inside an <xref:Microsoft.AspNetCore.Components.Forms.EditForm> component, just as before.
1567+
1568+
To opt into the nested objects and collection types validation feature:
1569+
1570+
<!-- UPDATE 10.0 - API cross-links -->
1571+
1572+
1. Call the `AddValidation` extension method in the `Program` file where services are registered.
1573+
2. Declare the form model types in a C# class file, not in a Razor component (`.razor`).
1574+
3. Annotate the root form model type with the `[ValidatableType]` attribute.
1575+
1576+
Without following the preceding steps, form validation behavior doesn't include nested model and collection type validation.
1577+
1578+
<!-- UPDATE 10.0 - Replace with a fully working, cut-'n-paste example -->
1579+
1580+
The following example demonstrates customer orders with the improved form validation (details omitted for brevity):
1581+
1582+
In `Program.cs`, call `AddValidation` on the service collection:
1583+
1584+
```csharp
1585+
builder.Services.AddValidation();
1586+
```
1587+
1588+
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.
1589+
1590+
`Order.cs`:
1591+
1592+
```csharp
1593+
[ValidatableType]
1594+
public class Order
1595+
{
1596+
public Customer Customer { get; set; } = new();
1597+
public List<OrderItem> OrderItems { get; set; } = [];
1598+
}
1599+
1600+
public class Customer
1601+
{
1602+
[Required(ErrorMessage = "Name is required.")]
1603+
public string? FullName { get; set; }
1604+
1605+
[Required(ErrorMessage = "Email is required.")]
1606+
public string? Email { get; set; }
1607+
1608+
public ShippingAddress ShippingAddress { get; set; } = new();
1609+
}
1610+
```
1611+
1612+
In the following `OrderPage` component, the <xref:Microsoft.AspNetCore.Components.Forms.DataAnnotationsValidator> component is present in the <xref:Microsoft.AspNetCore.Components.Forms.EditForm> component.
1613+
1614+
`OrderPage.razor`:
1615+
1616+
```razor
1617+
<EditForm Model="Model">
1618+
<DataAnnotationsValidator />
1619+
1620+
<h3>Customer Details</h3>
1621+
<div class="mb-3">
1622+
<label>
1623+
Full Name
1624+
<InputText @bind-Value="Model!.Customer.FullName" />
1625+
</label>
1626+
<ValidationMessage For="@(() => Model!.Customer.FullName)" />
1627+
</div>
1628+
1629+
// ... form continues ...
1630+
</EditForm>
1631+
1632+
@code {
1633+
public Order? Model { get; set; }
1634+
1635+
protected override void OnInitialized() => Model ??= new();
1636+
}
1637+
```
1638+
1639+
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.
1640+
1641+
## Complex types
1642+
1643+
Blazor provides support for validating form input using data annotations with the built-in <xref:Microsoft.AspNetCore.Components.Forms.DataAnnotationsValidator>. However, the <xref:Microsoft.AspNetCore.Components.Forms.DataAnnotationsValidator> only validates top-level properties of the model bound to the form that aren't complex-type properties.
1644+
1645+
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.
1646+
1647+
> [!NOTE]
1648+
> 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.
1649+
1650+
:::moniker-end
1651+
1652+
:::moniker range="< aspnetcore-10.0"
1653+
1654+
## Nested objects, collection types, and complex types
15611655

15621656
Blazor provides support for validating form input using data annotations with the built-in <xref:Microsoft.AspNetCore.Components.Forms.DataAnnotationsValidator>. However, the <xref:Microsoft.AspNetCore.Components.Forms.DataAnnotationsValidator> only validates top-level properties of the model bound to the form that aren't collection- or complex-type properties.
15631657

15641658
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:
15651659

1660+
:::moniker-end
1661+
15661662
```razor
15671663
<EditForm ...>
15681664
<ObjectGraphDataAnnotationsValidator />

aspnetcore/blazor/fundamentals/routing.md

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -708,14 +708,33 @@ For more information on component disposal, see <xref:blazor/components/componen
708708

709709
<!-- UPDATE 10.0 - API doc cross-links -->
710710

711-
*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.*
712-
713711
<xref:Microsoft.AspNetCore.Components.NavigationManager> 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:
714712

715713
* **Static SSR**: Calling `NotFound` sets the HTTP status code to 404.
716-
* **Streaming rendering**: Throws an exception if the response has already started.
714+
717715
* **Interactive rendering**: Signals the Blazor router ([`Router` component](xref:blazor/fundamentals/routing#route-templates)) to render Not Found content.
718716

717+
* **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.
718+
719+
> [!NOTE]
720+
> 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.
721+
722+
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) (<xref:Microsoft.AspNetCore.Builder.StatusCodePagesExtensions.UseStatusCodePagesWithReExecute%2A>). The Not Found render fragment (`<NotFound>...</NotFound>`) and the `DefaultNotFound` 404 content ("`Not found`" plain text) don't have routes, so they can't be used during streaming rendering.
723+
724+
Streaming `NavigationManager.NotFound` content rendering uses (in order):
725+
726+
* A `NotFoundPage` passed to the `Router` component, if present.
727+
* A Status Code Pages Re-execution Middleware page, if configured.
728+
* No action if neither of the preceding approaches is adopted.
729+
730+
Non-streaming `NavigationManager.NotFound` content rendering uses (in order):
731+
732+
* A `NotFoundPage` passed to the `Router` component, if present.
733+
* Not Found render fragment content, if present. *Not recommended in .NET 10 or later.*
734+
* `DefaultNotFound` 404 content ("`Not found`" plain text).
735+
736+
[Status Code Pages Re-execution Middleware](xref:fundamentals/error-handling#usestatuscodepageswithreexecute) with <xref:Microsoft.AspNetCore.Builder.StatusCodePagesExtensions.UseStatusCodePagesWithReExecute%2A> 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.
737+
719738
When a component is rendered statically (static SSR) and `NavigationManager.NotFound` is called, the 404 status code is set on the response:
720739

721740
```razor
@@ -743,7 +762,7 @@ To provide Not Found content for global interactive rendering, use a Not Found p
743762
<p>Sorry! Nothing to show.</p>
744763
```
745764

746-
Assign the `NotFound` component to the router's `NotFoundPage` parameter. `NotFoundPage` supports routing that can be used across re-execution middleware, including non-Blazor middleware. If the `NotFound` render fragment is defined together with `NotFoundPage`, the page has higher priority.
765+
Assign the `NotFound` component to the router's `NotFoundPage` parameter. `NotFoundPage` supports routing that can be used across Status Code Pages Re-execution Middleware, including non-Blazor middleware. If the `NotFound` render fragment (`<NotFound>...</NotFound>`) is defined together with `NotFoundPage`, the page has higher priority.
747766

748767
In the following example, the preceding `NotFound` component is present in the app's `Pages` folder and passed to the `NotFoundPage` parameter:
749768

@@ -779,7 +798,7 @@ You can use the `OnNotFound` event for notifications when `NotFound` is invoked.
779798

780799
<!-- UPDATE 10.0 - For Pre5, the following can be expanded to
781800
cover CSR with an added bit of coverage for
782-
re-execution middleware. -->
801+
Re-execution Middleware. -->
783802

784803
In the following example for components that adopt [interactive server-side rendering (interactive SSR)](xref:blazor/fundamentals/index#client-and-server-rendering-concepts), custom content is rendered depending on where `OnNotFound` is called. If the event is triggered by the following `Movie` component when a movie isn't found on component initialization, a custom message states that the requested movie isn't found. If the event is triggered by the `User` component, a different message states that the user isn't found.
785804

aspnetcore/blazor/host-and-deploy/index.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,40 @@ For more information on *solutions*, see <xref:blazor/tooling#visual-studio-solu
106106

107107
:::moniker-end
108108

109+
:::moniker range=">= aspnetcore-10.0"
110+
111+
## JavaScript bundler support
112+
113+
The Blazor runtime relies on JavaScript (JS) files, the .NET runtime compiled into WebAssembly code, and managed assemblies packed as WebAssembly files. When a Blazor app is built, the Blazor runtime depends on these files from different build locations. Due to this constraint, Blazor's build output isn't compatible with JS bundlers, such as [Gulp](https://gulpjs.com), [Webpack](https://webpack.js.org), and [Rollup](https://rollupjs.org/).
114+
115+
To produce build output compatible with JS bundlers *during publish*, set the `WasmBundlerFriendlyBootConfig` MSBuild property to `true` in the app's project file:
116+
117+
```xml
118+
<WasmBundlerFriendlyBootConfig>true</WasmBundlerFriendlyBootConfig>
119+
```
120+
121+
> [!IMPORTANT]
122+
> This feature only produces the bundler-friendly output when publishing the app.
123+
124+
The output isn't directly runnable in the browser, but it can be consumed by JS tools to bundle JS files with the rest of the developer-supplied scripts.
125+
126+
When `WasmBundlerFriendlyBootConfig` is enabled, the produced JS contains `import` directives for all of the assets in the app, which makes the dependencies visible for the bundler. Many of the assets aren't loadable by the browser, but bundlers usually can be configured to recognize the assets by their file type to handle loading. For details on how to configure your bundler, refer to the bundler's documentation.
127+
128+
> [!NOTE]
129+
> Bundling build output should be possible by mapping imports to individual file locations using a JS bundler custom plugin. We don't provide such a plugin at the moment.
130+
131+
> [!NOTE]
132+
> Replacing the `files` plugin with `url`, all of the app's JS files, including the Blazor-WebAssembly runtime (base64 encoded in the JS), 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 JS bundler processing.
133+
134+
The following sample apps are based on [Rollup](https://rollupjs.org/). Similar concepts apply when using other JS bundlers.
135+
136+
Demonstration sample apps:
137+
138+
* [Blazor WASM in a React application (`maraf/blazor-wasm-react` GitHub repository)](https://github.com/maraf/blazor-wasm-react)
139+
* [.NET on WASM in a React component sample (`maraf/dotnet-wasm-react` GitHub repository)](https://github.com/maraf/dotnet-wasm-react)
140+
141+
:::moniker-end
142+
109143
## Blazor Server `MapFallbackToPage` configuration
110144

111145
*This section only applies to Blazor Server apps. <xref:Microsoft.AspNetCore.Builder.RazorPagesEndpointRouteBuilderExtensions.MapFallbackToPage%2A> isn't supported in Blazor Web Apps and Blazor WebAssembly apps.*

aspnetcore/blazor/host-and-deploy/server/index.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,23 @@ For guidance on building secure and scalable server-side Blazor apps, see the fo
4040

4141
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).
4242

43+
:::moniker range=">= aspnetcore-10.0"
44+
45+
## Blazor WebAssembly static asset preloading
46+
47+
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 (`<base>`):
48+
49+
```razor
50+
<LinkPreload />
51+
```
52+
53+
A Razor component is used instead of `<link>` elements because:
54+
55+
* The component permits the base URL (`<base>` tag's `href` attribute value) to correctly identify the root of the Blazor app within an ASP.NET Core app.
56+
* 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.
57+
58+
:::moniker-end
59+
4360
## SignalR configuration
4461

4562
[SignalR's hosting and scaling conditions](xref:signalr/publish-to-azure-web-app) apply to Blazor apps that use SignalR.

0 commit comments

Comments
 (0)