diff --git a/aspnetcore/blazor/forms/validation.md b/aspnetcore/blazor/forms/validation.md index 978e280e1800..640c178a175a 100644 --- a/aspnetcore/blazor/forms/validation.md +++ b/aspnetcore/blazor/forms/validation.md @@ -5,7 +5,7 @@ description: Learn how to use validation in Blazor forms. monikerRange: '>= aspnetcore-3.1' ms.author: wpickett ms.custom: mvc -ms.date: 11/12/2024 +ms.date: 09/08/2025 uid: blazor/forms/validation --- # ASP.NET Core Blazor forms validation @@ -134,6 +134,12 @@ The compon * [`DataAnnotationsValidator`](https://github.com/dotnet/AspNetCore/blob/main/src/Components/Forms/src/DataAnnotationsValidator.cs) * [`EnableDataAnnotationsValidation`](https://github.com/dotnet/AspNetCore/blob/main/src/Components/Forms/src/EditContextDataAnnotationsExtensions.cs) +:::moniker range=">= aspnetcore-10.0" + +For details on validation behavior, see the [`DataAnnotationsValidator` validation behavior](#dataannotationsvalidator-validation-behavior) section. + +:::moniker-end + If you need to enable data annotations validation support for an in code, call with an injected (`@inject IServiceProvider ServiceProvider`) on the . For an advanced example, see the [`NotifyPropertyChangedValidationComponent` component in the ASP.NET Core Blazor framework's `BasicTestApp` (`dotnet/aspnetcore` GitHub repository)](https://github.com/dotnet/aspnetcore/blob/main/src/Components/test/testassets/BasicTestApp/FormsTest/NotifyPropertyChangedValidationComponent.razor). In a production version of the example, replace the `new TestServiceProvider()` argument for the service provider with an injected . [!INCLUDE[](~/includes/aspnetcore-repo-ref-source-links.md)] @@ -1638,26 +1644,18 @@ In the following `OrderPage` component, the . 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. +> [!NOTE] +> For apps targeting .NET 10 or later, we no longer recommend using the *experimental* [`Microsoft.AspNetCore.Components.DataAnnotations.Validation`](https://www.nuget.org/packages/Microsoft.AspNetCore.Components.DataAnnotations.Validation) package and approach described in this section. We recommend using the built-in validation features of the the component. -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: +Blazor provides support for validating form input using data annotations with the built-in . However, the in .NET 9 or earlier only validates top-level properties of the model bound to the form that aren't collection- or complex-type properties. -:::moniker-end +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 in .NET 9 or earlier: ```razor @@ -1703,6 +1701,8 @@ public class ShipDescription } ``` +:::moniker-end + ## Enable the submit button based on form validation To enable and disable the submit button based on form validation, the following example: @@ -1829,3 +1829,17 @@ A side effect of the preceding approach is that a validation summary ( component has the same validation order and short-circuiting behavior as . The following rules are applied when validating an instance of type `T`: + +1. Member properties of `T` are validated, including recursively validating nested objects. +1. Type-level attributes of `T` are validated. +1. The method is executed, if `T` implements it. + +If one of the preceding steps produces a validation error, the remaining steps are skipped. + +:::moniker-end diff --git a/aspnetcore/blazor/fundamentals/routing.md b/aspnetcore/blazor/fundamentals/routing.md index cee45dab7dba..3aeb07577c81 100644 --- a/aspnetcore/blazor/fundamentals/routing.md +++ b/aspnetcore/blazor/fundamentals/routing.md @@ -5,7 +5,7 @@ description: Learn how to manage Blazor app request routing and how to use the N monikerRange: '>= aspnetcore-3.1' ms.author: wpickett ms.custom: mvc -ms.date: 11/12/2024 +ms.date: 09/08/2025 uid: blazor/fundamentals/routing --- # ASP.NET Core Blazor routing and navigation @@ -704,6 +704,31 @@ The following component: For more information on component disposal, see . +:::moniker range=">= aspnetcore-9.0" + +## Navigation Manager redirect behavior during static server-side rendering (static SSR) + +For a redirect during static server-side rendering (static SSR), relies on throwing a that gets captured by the framework, which converts the error into a redirect. Code that exists after the call to isn't called. When using Visual Studio, the debugger breaks on the exception, requiring you to deselect the checkbox for **Break when this exception type is user-handled** in the Visual Studio UI to avoid the debugger stopping for future redirects. + +:::moniker-end + +:::moniker range=">= aspnetcore-10.0" + +You can use the `` MSBuild property set to `true` in the app's project file to opt-in to no longer throwing a . This behavior is enabled by default in the .NET 10 or later Blazor Web App project template: + +```xml +true +``` + +:::moniker-end + +:::moniker range=">= aspnetcore-9.0 < aspnetcore-10.0" + +> [!NOTE] +> In .NET 10 or later, you can opt-in to not throwing a by setting the `` MSBuild property to `true` in the app's project file. To take advantage of the new MSBuild property and behavior, upgrade the app to .NET 10 or later. + +:::moniker-end + :::moniker range=">= aspnetcore-10.0" ## Not Found responses diff --git a/aspnetcore/blazor/host-and-deploy/configure-trimmer.md b/aspnetcore/blazor/host-and-deploy/configure-trimmer.md index e369c66d8a72..f535c74a69fd 100644 --- a/aspnetcore/blazor/host-and-deploy/configure-trimmer.md +++ b/aspnetcore/blazor/host-and-deploy/configure-trimmer.md @@ -5,7 +5,7 @@ description: Learn how to control the Intermediate Language (IL) Trimmer when bu monikerRange: '>= aspnetcore-5.0' ms.author: wpickett ms.custom: mvc -ms.date: 11/12/2024 +ms.date: 09/08/2025 uid: blazor/host-and-deploy/configure-trimmer --- # Configure the Trimmer for ASP.NET Core Blazor @@ -42,58 +42,107 @@ For more information, see [Trimming options (.NET documentation)](/dotnet/core/d ## Failure to preserve types used by a published app -Trimming may have detrimental effects for a published app leading to runtime errors. In apps that use [reflection](/dotnet/csharp/advanced-topics/reflection-and-attributes/), the IL Trimmer often can't determine the required types for runtime reflection and trims them away or trims away parameter names from methods. This can happen with complex framework types used for JS interop, JSON serialization/deserialization, and other operations. +Trimming may have detrimental effects for a published app leading to runtime errors, even in spite of setting the [`` property](#configuration) to `false` in the project file. In apps that use [reflection](/dotnet/csharp/advanced-topics/reflection-and-attributes/), the IL Trimmer often can't determine the required types for runtime reflection and trims them away or trims away parameter names from methods. This can happen with complex framework types used for JS interop, JSON serialization/deserialization, and other operations. The IL Trimmer is also unable to react to an app's dynamic behavior at runtime. To ensure the trimmed app works correctly once deployed, test published output frequently while developing. -Consider the following client-side component in a Blazor Web App (.NET 8 or later) that deserializes a collection (`List>`): +Consider the following example that performs JSON deserialization into a collection (`List>`). + +`TrimExample.razor`: ```razor -@rendermode @(new InteractiveWebAssemblyRenderMode(false)) +@page "/trim-example" @using System.Diagnostics.CodeAnalysis @using System.Text.Json -
+

Trim Example

+ +
    @foreach (var item in @items) { -
    @item.Key
    -
    @item.Value
    +
  • @item.Item1, @item.Item2
  • } -
+ @code { - private List> items = []; + private List> items = []; [StringSyntax(StringSyntaxAttribute.Json)] private const string data = - """[{"key":"key 1","value":"value 1"},{"key":"key 2","value":"value 2"}]"""; + """[{"item1":"1:T1","item2":"1:T2"},{"item1":"2:T1","item2":"2:T2"}]"""; protected override void OnInitialized() { JsonSerializerOptions options = new() { PropertyNameCaseInsensitive = true }; items = JsonSerializer - .Deserialize>>(data, options)!; + .Deserialize>>(data, options)!; } } ``` -The preceding component executes normally when the app is run locally and produces the following rendered definition list (`
`): +The preceding component executes normally when the app is run locally and produces the following rendered list: + +> • 1:T1, 1:T2 +> • 2:T2, 2:T2 + +When the app is published, is trimmed from the app, even in spite of setting the `` property to `false` in the project file. Accessing the component throws the following exception: + +> :::no-loc text="crit: Microsoft.AspNetCore.Components.WebAssembly.Rendering.WebAssemblyRenderer[100]"::: +> :::no-loc text="Unhandled exception rendering component: ConstructorContainsNullParameterNames, System.Tuple`2[System.String,System.String]"::: +> :::no-loc text="System.NotSupportedException: ConstructorContainsNullParameterNames, System.Tuple`2[System.String,System.String]"::: + +To address lost types, consider adopting one of the following approaches. + +### Custom types + +Custom types aren't trimmed by Blazor when an app is published, so we recommend using custom types for JS interop, JSON serialization/deserialization, and other operations that rely on reflection. + +The following modifications create a `StringTuple` type for use by the component. + +`StringTuple.cs`: + +```csharp +[method: SetsRequiredMembers] +public sealed class StringTuple(string item1, string item2) +{ + public required string Item1 { get; init; } = item1; + public required string Item2 { get; init; } = item2; +} +``` + +The component is modified to use the `StringTuple` type: + +```diff +- private List> items = []; ++ private List items = []; +``` + +```diff +- items = JsonSerializer.Deserialize>>(data, options)!; ++ items = JsonSerializer.Deserialize>(data, options)!; +``` + +Because custom types are never trimmed by Blazor when an app is published, the component works as designed after the app is published. + +:::moniker range=">= aspnetcore-10.0" -> **:::no-loc text="key 1":::** -> :::no-loc text="value 1"::: -> **:::no-loc text="key 2":::** -> :::no-loc text="value 2"::: +If you prefer to use framework types in spite of our recommendation, use either of the following approaches: -When the app is published, is trimmed from the app, even in spite of setting the [`` property](#configuration) to `false` in the project file. Accessing the component throws the following exception: +* [Preserve the type as a dynamic dependency](#preserve-the-type-as-a-dynamic-dependency) +* [Use a Root Descriptor](#use-a-root-descriptor) -> :::no-loc text="Unhandled exception rendering component: ConstructorContainsNullParameterNames, System.Collections.Generic.KeyValuePair`2[System.String,System.String]"::: +:::moniker-end -To address lost types, consider the following approaches. +:::moniker range="< aspnetcore-10.0" + +If you prefer to use framework types in spite of our recommendation, [preserve the type as a dynamic dependency](#preserve-the-type-as-a-dynamic-dependency). + +:::moniker-end ### Preserve the type as a dynamic dependency -We recommend creating a dynamic dependency to preserve the type with the [`[DynamicDependency]` attribute](xref:System.Diagnostics.CodeAnalysis.DynamicDependencyAttribute). +Create a dynamic dependency to preserve the type with the [`[DynamicDependency]` attribute](xref:System.Diagnostics.CodeAnalysis.DynamicDependencyAttribute). If not already present, add an `@using` directive for : @@ -101,14 +150,15 @@ If not already present, add an `@using` directive for : +Add a [`[DynamicDependency]` attribute](xref:System.Diagnostics.CodeAnalysis.DynamicDependencyAttribute) to preserve the : ```diff -+ [DynamicDependency(DynamicallyAccessedMemberTypes.PublicConstructors, typeof(KeyValuePair))] -private List> items = []; ++ [DynamicDependency(DynamicallyAccessedMemberTypes.PublicConstructors, ++ typeof(Tuple))] +private List> items = []; ``` - - -### Custom types - - - -The following modifications create a `StringKeyValuePair` type for use by the component. - -`StringKeyValuePair.cs`: +:::moniker-end -```csharp -[method: SetsRequiredMembers] -public sealed class StringKeyValuePair(string key, string value) -{ - public required string Key { get; init; } = key; - public required string Value { get; init; } = value; -} -``` +:::moniker range="= aspnetcore-8.0" -The component is modified to use the `StringKeyValuePair` type: +### Workaround in .NET 8 -```diff -- private List> items = []; -+ private List items = []; -``` +As a workaround in .NET 8, you can add the `_ExtraTrimmerArgs` MSBuild property set to `--keep-metadata parametername` in the app's project file to preserve parameter names during trimming: -```diff -- items = JsonSerializer.Deserialize>>(data, options)!; -+ items = JsonSerializer.Deserialize>(data, options)!; +```xml + + <_ExtraTrimmerArgs>--keep-metadata parametername + ``` -Because custom types are never trimmed by Blazor when an app is published, the component works as designed after the app is published. +:::moniker-end ## Additional resources diff --git a/aspnetcore/blazor/javascript-interoperability/index.md b/aspnetcore/blazor/javascript-interoperability/index.md index 297b16dd8aed..30f0f22f5fbf 100644 --- a/aspnetcore/blazor/javascript-interoperability/index.md +++ b/aspnetcore/blazor/javascript-interoperability/index.md @@ -202,7 +202,7 @@ Blazor uses for serialization w * Global default serialization isn't customizable to avoid breaking existing component libraries, impacts on performance and security, and reductions in reliability. * Serializing .NET member names results in lowercase JSON key names. * JSON is deserialized as C# instances, which permit mixed casing. Internal casting for assignment to C# model properties works as expected in spite of any case differences between JSON key names and C# property names. -* Complex framework types, such as , might be [trimmed away by the IL Trimmer on publish](xref:blazor/host-and-deploy/configure-trimmer#failure-to-preserve-types-used-by-a-published-app) and not present for JS interop or JSON serialization/deserialization. We recommend creating custom types for types that the IL Trimmer trims away. +* Complex framework types might be [trimmed away by the IL Trimmer on publish](xref:blazor/host-and-deploy/configure-trimmer#failure-to-preserve-types-used-by-a-published-app) and not present for JS interop or JSON serialization/deserialization. We recommend creating custom types for types that the IL Trimmer trims away. * Blazor always relies on [reflection for JSON serialization](/dotnet/standard/serialization/system-text-json/reflection-vs-source-generation), including when using C# [source generation](/dotnet/csharp/roslyn-sdk/source-generators-overview). Setting `JsonSerializerIsReflectionEnabledByDefault` to `false` in the app's project file results in an error when serialization is attempted. API is available for custom serialization. Properties can be annotated with a [`[JsonConverter]` attribute](xref:System.Text.Json.Serialization.JsonConverterAttribute) to override default serialization for an existing data type. diff --git a/aspnetcore/blazor/state-management/prerendered-state-persistence.md b/aspnetcore/blazor/state-management/prerendered-state-persistence.md index 6114ad55b287..14228127d395 100644 --- a/aspnetcore/blazor/state-management/prerendered-state-persistence.md +++ b/aspnetcore/blazor/state-management/prerendered-state-persistence.md @@ -5,7 +5,7 @@ description: Learn how to persist user data (state) in Blazor apps using Blazor' monikerRange: '>= aspnetcore-8.0' ms.author: wpickett ms.custom: mvc -ms.date: 08/05/2025 +ms.date: 09/08/2025 uid: blazor/state-management/prerendered-state-persistence --- # ASP.NET Core Blazor prerendered state persistence @@ -312,13 +312,47 @@ For components embedded into a page or view of a Razor Pages or MVC app, you mus ## Interactive routing and prerendering + + When the `Routes` component doesn't define a render mode, the app is using per-page/component interactivity and navigation. Using per-page/component navigation, internal navigation is handled by [enhanced routing](xref:blazor/fundamentals/routing#enhanced-navigation-and-form-handling) after the app becomes interactive. "Internal navigation" in this context means that the URL destination of the navigation event is a Blazor endpoint inside the app. - +:::moniker range=">= aspnetcore-10.0" + +Blazor supports handling persistent component state during [enhanced navigation](xref:blazor/fundamentals/routing#enhanced-navigation-and-form-handling). State persisted during enhanced navigation can be read by interactive components on the page. + +By default, persistent component state is only loaded by interactive components when they're initially loaded on the page. This prevents important state, such as data in an edited webform, from being overwritten if additional enhanced navigation events to the same page occur after the component is loaded. + +If the data is read-only and doesn't change frequently, opt-in to allow updates during enhanced navigation by setting `AllowUpdates = true` on the [`[PersistentState]` attribute](xref:Microsoft.AspNetCore.Components.PersistentStateAttribute). This is useful for scenarios such as displaying cached data that's expensive to fetch but doesn't change often. The following example demonstrates the use of `AllowUpdates` for weather forecast data: + +```csharp +[PersistentState(AllowUpdates = true)] +public WeatherForecast[]? Forecasts { get; set; } + +protected override async Task OnInitializedAsync() +{ +    Forecasts ??= await ForecastService.GetForecastAsync(); +} +``` + +To skip restoring state during prerendering, set `RestoreBehavior` to `SkipInitialValue`: + +```csharp +[PersistentState(RestoreBehavior = RestoreBehavior.SkipInitialValue)] +public string NoPrerenderedData { get; set; } +``` + +To skip restoring state during reconnection, set `RestoreBehavior` to `SkipLastSnapshot`. This can be useful to ensure fresh data after reconnection: + +```csharp +[PersistentState(RestoreBehavior = RestoreBehavior.SkipLastSnapshot)] +public int CounterNotRestoredOnReconnect { get; set; } +``` + +Call `PersistentComponentState.RegisterOnRestoring` to register a callback for imperatively controlling how state is restored, similar to how provides full control of how state is persisted. + +:::moniker-end + +:::moniker range="< aspnetcore-10.0" The service only works on the initial page load and not across internal enhanced page navigation events. @@ -326,4 +360,6 @@ If the app performs a full (non-enhanced) navigation to a page utilizing persist If an interactive circuit has already been established and an enhanced navigation is performed to a page utilizing persistent component state, the state *isn't made available in the existing circuit for the component to use*. There's no prerendering for the internal page request, and the service isn't aware that an enhanced navigation has occurred. There's no mechanism to deliver state updates to components that are already running on an existing circuit. The reason for this is that Blazor only supports passing state from the server to the client at the time the runtime initializes, not after the runtime has started. -Disabling enhanced navigation, which reduces performance but also avoids the problem of loading state with for internal page requests, is covered in . +Disabling enhanced navigation, which reduces performance but also avoids the problem of loading state with for internal page requests, is covered in . Alternatively, update the app to .NET 10 or later, where Blazor supports handling persistent component state when during enhanced navigation. + +:::moniker-end diff --git a/aspnetcore/fundamentals/minimal-apis.md b/aspnetcore/fundamentals/minimal-apis.md index 9c2a86ff3291..c2089186bdab 100644 --- a/aspnetcore/fundamentals/minimal-apis.md +++ b/aspnetcore/fundamentals/minimal-apis.md @@ -1,11 +1,11 @@ --- title: Minimal APIs quick reference author: wadepickett -description: Provides an overview of minimal APIs in ASP.NET Core +description: Provides an overview of Minimal APIs in ASP.NET Core ms.author: wpickett content_well_notification: AI-contribution monikerRange: '>= aspnetcore-6.0' -ms.date: 08/22/2025 +ms.date: 09/08/2025 uid: fundamentals/minimal-apis ai-usage: ai-assisted --- @@ -20,19 +20,19 @@ ai-usage: ai-assisted This document: -* Provides a quick reference for minimal APIs. +* Provides a quick reference for Minimal APIs. * Is intended for experienced developers. For an introduction, see . -The minimal APIs consist of: +The Minimal APIs consist of: -* [WebApplication and WebApplicationBuilder](xref:fundamentals/minimal-apis/webapplication) +* [`WebApplication` and `WebApplicationBuilder`](xref:fundamentals/minimal-apis/webapplication) * [Route Handlers](xref:fundamentals/minimal-apis/route-handlers) [!INCLUDE[](~/fundamentals/minimal-apis/includes/webapplication10.md)] ## ASP.NET Core Middleware -The following table lists some of the middleware frequently used with minimal APIs. +The following table lists some of the middleware frequently used with Minimal APIs. | Middleware | Description | API | |--|--|--| @@ -72,7 +72,7 @@ The arguments passed to these methods are called "route h ## Validation support in Minimal APIs -Support for validation in Minimal APIs is now available. This feature allows you to request validation of data sent to your API endpoints. Enabling validation allows the ASP.NET Core runtime to perform any validations defined on the: +Enabling validation allows the ASP.NET Core runtime to perform validations defined on the: * Query * Header @@ -89,20 +89,20 @@ public record Product( ``` Developers customize the behavior of the validation system by: -* Creating custom [`[Validation]`](xref:System.ComponentModel.DataAnnotations.ValidationAttribute) attribute implementations. +* Creating custom [`[Validation]` attribute](xref:System.ComponentModel.DataAnnotations.ValidationAttribute) implementations. * Implementing the [`IValidatableObject`](xref:System.ComponentModel.DataAnnotations.IValidatableObject) interface for complex validation logic. -If validation fails, the runtime returns a 400 Bad Request response with details of the validation errors. +If validation fails, the runtime returns a *400 - Bad Request* response with details of the validation errors. -### Enable built-in validation support for minimal APIs +### Enable built-in validation support for Minimal APIs -Enable the built-in validation support for minimal APIs by calling the `AddValidation` extension method to register the required services in the service container for your application: +Enable the built-in validation support for Minimal APIs by calling the `AddValidation` extension method to register the required services in the service container for your application: ```csharp builder.Services.AddValidation(); ``` -The implementation automatically discovers types that are defined in minimal API handlers or as base types of types defined in minimal API handlers. An endpoint filter performs validation on these types and is added for each endpoint. +The implementation automatically discovers types that are defined in Minimal API handlers or as base types of types defined in Minimal API handlers. An endpoint filter performs validation on these types and is added for each endpoint. Validation can be disabled for specific endpoints by using the `DisableValidation` extension method, as in the following example: @@ -114,7 +114,7 @@ app.MapPost("/products", ``` ### Customize validation error responses using IProblemDetailsService -Customize error responses from minimal API validation logic with an implementation. Register this service in your application's service collection to enable more consistent and user-specific error responses. Support for minimal API validation was introduced in ASP.NET Core in .NET 10. +Customize error responses from Minimal API validation logic with an implementation. Register this service in your application's service collection to enable more consistent and user-specific error responses. Support for Minimal API validation was introduced in ASP.NET Core in .NET 10. To implement custom validation error responses: @@ -253,7 +253,7 @@ We recommend adding an extension method to interface can represent values returned from minimal APIs that don't utilize the implicit support for JSON serializing the returned object to the HTTP response. The static [Results](/dotnet/api/microsoft.aspnetcore.http.results) class is used to create varying `IResult` objects that represent different types of responses. For example, setting the response status code or redirecting to another URL. +The interface can represent values returned from Minimal APIs that don't utilize the implicit support for JSON serializing the returned object to the HTTP response. The static [Results](/dotnet/api/microsoft.aspnetcore.http.results) class is used to create varying `IResult` objects that represent different types of responses. For example, setting the response status code or redirecting to another URL. The types implementing `IResult` are public, allowing for type assertions when testing. For example: @@ -333,7 +333,7 @@ The following code disables `ValidateScopes` and `ValidateOnBuild` in `Developme * [Short-circuit routing](https://andrewlock.net/exploring-the-dotnet-8-preview-short-circuit-routing/) * [Identity API endpoints](https://andrewlock.net/exploring-the-dotnet-8-preview-introducing-the-identity-api-endpoints/) * [Keyed service dependency injection container support](https://andrewlock.net/exploring-the-dotnet-8-preview-keyed-services-dependency-injection-support/) -* [A look behind the scenes of minimal API endpoints](https://andrewlock.net/behind-the-scenes-of-minimal-apis-1-a-first-look-behind-the-scenes-of-minimal-api-endpoints/) +* [A look behind the scenes of Minimal API endpoints](https://andrewlock.net/behind-the-scenes-of-minimal-apis-1-a-first-look-behind-the-scenes-of-minimal-api-endpoints/) * [Organizing ASP.NET Core Minimal APIs](https://www.tessferrandez.com/blog/2023/10/31/organizing-minimal-apis.html) * [Fluent validation discussion on GitHub](https://github.com/dotnet/aspnetcore/issues/51834#issuecomment-1837180853) diff --git a/aspnetcore/migration/90-to-100.md b/aspnetcore/migration/90-to-100.md index 298cdf80229e..5e47dad2d25b 100644 --- a/aspnetcore/migration/90-to-100.md +++ b/aspnetcore/migration/90-to-100.md @@ -14,12 +14,6 @@ This article explains how to update an ASP.NET Core in .NET 9 to ASP.NET Core in ## Prerequisites - - # [Visual Studio](#tab/visual-studio) [!INCLUDE[](~/includes/net-prereqs-vs-10-latest.md)] diff --git a/aspnetcore/migration/90-to-100/includes/blazor.md b/aspnetcore/migration/90-to-100/includes/blazor.md index 4598252c3211..7056326c82f3 100644 --- a/aspnetcore/migration/90-to-100/includes/blazor.md +++ b/aspnetcore/migration/90-to-100/includes/blazor.md @@ -3,3 +3,24 @@ Complete migration coverage for Blazor apps is scheduled for September and Octob ### Adopt passkey user authentication in an existing Blazor Web App For guidance, see . + +### When navigation errors are disabled in a Blazor Web App with Individual Accounts + +*This section applies to Blazor Web Apps that set the `` MSBuild property to `true` in order to avoid throwing an navigation exception during static server-side rendering (static SSR).* + +The `IdentityRedirectManager` threw an in the `RedirectTo` method to ensure the method wasn't called from an interactive render mode and all the redirection methods were marked with the [`[DoesNotReturn]` attribute](xref:System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute). The .NET 10 or later Blazor Web App project template sets the `` MSBuild property to `true` in the app's project file in order to avoid throwing the exception during static SSR. If an app based on the project template from a prior release of .NET is updated to .NET 10 or later and includes the `` MSBuild property set to `true`, make the following changes. For more information, see . + +In `Components/Account/IdentityRedirectManager.cs`: + +* Remove the from the `RedirectTo` method: + + ```diff + - throw new InvalidOperationException( + - $"{nameof(IdentityRedirectManager)} can only be used during static rendering."); + ``` + +* Remove five instances of the the `[DoesNotReturn]` attribute from the file: + + ```diff + - [DoesNotReturn] + ``` diff --git a/aspnetcore/mvc/models/validation.md b/aspnetcore/mvc/models/validation.md index a29e3fb61e16..c03c7eac3e0b 100644 --- a/aspnetcore/mvc/models/validation.md +++ b/aspnetcore/mvc/models/validation.md @@ -44,21 +44,7 @@ Validation attributes let you specify validation rules for model properties. The ## Built-in attributes -Here are some of the built-in validation attributes: - -* [[ValidateNever]](xref:Microsoft.AspNetCore.Mvc.ModelBinding.Validation.ValidateNeverAttribute): Indicates that a property or parameter should be excluded from validation. -* [[CreditCard]](xref:System.ComponentModel.DataAnnotations.CreditCardAttribute): Validates that the property has a credit card format. Requires [jQuery Validation Additional Methods](https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.19.1/additional-methods.js). -* [[Compare]](xref:System.ComponentModel.DataAnnotations.CompareAttribute): Validates that two properties in a model match. -* [[EmailAddress]](xref:System.ComponentModel.DataAnnotations.EmailAddressAttribute): Validates that the property has an email format. -* [[Phone]](xref:System.ComponentModel.DataAnnotations.PhoneAttribute): Validates that the property has a telephone number format. -* [[Range]](xref:System.ComponentModel.DataAnnotations.RangeAttribute): Validates that the property value falls within a specified range. -* [[RegularExpression]](xref:System.ComponentModel.DataAnnotations.RegularExpressionAttribute): Validates that the property value matches a specified regular expression. -* [[Required]](xref:System.ComponentModel.DataAnnotations.RequiredAttribute): Validates that the field isn't null. See [`[Required]` attribute](#non-nullable-reference-types-and-required-attribute) for details about this attribute's behavior. -* [[StringLength]](xref:System.ComponentModel.DataAnnotations.StringLengthAttribute): Validates that a string property value doesn't exceed a specified length limit. -* [[Url]](xref:System.ComponentModel.DataAnnotations.UrlAttribute): Validates that the property has a URL format. -* [[Remote]](xref:Microsoft.AspNetCore.Mvc.RemoteAttribute): Validates input on the client by calling an action method on the server. See [`[Remote]` attribute](#remote-attribute) for details about this attribute's behavior. - -A complete list of validation attributes can be found in the namespace. +For a complete list of validation attributes, see the namespace. ### Error messages diff --git a/aspnetcore/release-notes/aspnetcore-10/includes/blazor.md b/aspnetcore/release-notes/aspnetcore-10/includes/blazor.md index 02638cd2a706..57d917eb7d4d 100644 --- a/aspnetcore/release-notes/aspnetcore-10/includes/blazor.md +++ b/aspnetcore/release-notes/aspnetcore-10/includes/blazor.md @@ -372,21 +372,23 @@ In Blazor Web Apps, framework static assets are automatically preloaded using [` For more information, see . -### `NavigationManager.NavigateTo` no longer throws a `NavigationException` +### Opt-in to avoiding a `NavigationException` during static server-side rendering with `NavigationManager.NavigateTo` -Previously, calling during static server-side rendering (SSR) would throw a , interrupting execution before being converted to a redirection response. This caused confusion during debugging and was inconsistent with interactive rendering, where code after continues to execute normally. +Calling during static server-side rendering (static SSR) throws a , interrupting execution before being converted to a redirection response. This can cause confusion during debugging and is inconsistent with interactive rendering behavior, where code after continues to execute normally. -Calling during static SSR no longer throws a . Instead, it behaves consistently with interactive rendering by performing the navigation without throwing an exception. +In .NET 10, you can set the `` MSBuild property to `true` in the app's project file in order to avoid throwing the exception during static SSR: -Code that relied on being thrown should be updated. For example, in the default Blazor Identity UI, the `IdentityRedirectManager` previously threw an after calling `RedirectTo` to ensure it wasn't invoked during interactive rendering. This exception and the [`[DoesNotReturn]` attributes](xref:System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute) should now be removed. +```xml + + true + +``` -To revert to the previous behavior of throwing a , set the following switch: +With the MSBuild property set, calling during static SSR no longer throws a . Instead, it behaves consistently with interactive rendering by performing the navigation without throwing an exception. Code after executes before the redirection occurs. -```csharp -AppContext.SetSwitch( - "Microsoft.AspNetCore.Components.Endpoints.NavigationManager.DisableThrowNavigationException", - isEnabled: false); -``` +The .NET 10 Blazor Web App project template sets the MSBuild property to `true` by default. We recommend that apps updating to .NET 10 use the new MSBuild property and avoid the prior behavior. + +If the MSBuild property is used, code that relied on being thrown should be updated. In the default Blazor Identity UI of the Blazor Web App project template before the release of .NET 10, the `IdentityRedirectManager` throws an after calling `RedirectTo` to ensure that the method wasn't invoked during interactive rendering. This exception and the [`[DoesNotReturn]` attributes](xref:System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute) should now be removed when the MSBuild property is used. For more information, see . ### Blazor router has a `NotFoundPage` parameter @@ -587,6 +589,21 @@ In the following `OrderPage` component, the implementation. + * The `[SkipValidation]` attribute can exclude properties or types from validation. +* Validation now uses a source generator-based implementation instead of reflection-based implementation for improved performance and compatibility with ahead-of-time (AOT) compilation. + +The component now has the same validation order and short-circuiting behavior as . The following rules are applied when validating an instance of type `T`: + +1. Member properties of `T` are validated, including recursively validating nested objects. +1. Type-level attributes of `T` are validated. +1. The method is executed, if `T` implements it. + +If one of the preceding steps produces a validation error, the remaining steps are skipped. + ### Custom Blazor cache and `BlazorCacheBootResources` MSBuild property removed Now that all Blazor client-side files are fingerprinted and cached by the browser, Blazor's custom caching mechanism and the `BlazorCacheBootResources` MSBuild property have been removed from the framework. If the client-side project's project file contains the MSBuild property, remove the property, as it no longer has any effect: @@ -708,3 +725,37 @@ In the following example, a hidden input field is created for the form's `Parame private void Submit() => submitted = true; } ``` + +### Persistent component state support for enhanced navigation + +Blazor now supports handling persistent component state during [enhanced navigation](xref:blazor/fundamentals/routing#enhanced-navigation-and-form-handling). State persisted during enhanced navigation can be read by interactive components on the page. + +By default, persistent component state is only loaded by interactive components when they're initially loaded on the page. This prevents important state, such as data in an edited webform, from being overwritten if additional enhanced navigation events to the same page occur after the component is loaded. + +If the data is read-only and doesn't change frequently, opt-in to allow updates during enhanced navigation by setting `AllowUpdates = true` on the [`[PersistentState]` attribute](xref:Microsoft.AspNetCore.Components.PersistentStateAttribute). This is useful for scenarios such as displaying cached data that's expensive to fetch but doesn't change often. The following example demonstrates the use of `AllowUpdates` for weather forecast data: + +```csharp +[PersistentState(AllowUpdates = true)] +public WeatherForecast[]? Forecasts { get; set; } + +protected override async Task OnInitializedAsync() +{ +    Forecasts ??= await ForecastService.GetForecastAsync(); +} +``` + +To skip restoring state during prerendering, set `RestoreBehavior` to `SkipInitialValue`: + +```csharp +[PersistentState(RestoreBehavior = RestoreBehavior.SkipInitialValue)] +public string NoPrerenderedData { get; set; } +``` + +To skip restoring state during reconnection, set `RestoreBehavior` to `SkipLastSnapshot`. This can be useful to ensure fresh data after reconnection: + +```csharp +[PersistentState(RestoreBehavior = RestoreBehavior.SkipLastSnapshot)] +public int CounterNotRestoredOnReconnect { get; set; } +``` + +Call `PersistentComponentState.RegisterOnRestoring` to register a callback for imperatively controlling how state is restored, similar to how provides full control of how state is persisted.