diff --git a/aspnetcore/blazor/components/virtualization.md b/aspnetcore/blazor/components/virtualization.md index e84db8887908..fff426b50956 100644 --- a/aspnetcore/blazor/components/virtualization.md +++ b/aspnetcore/blazor/components/virtualization.md @@ -274,6 +274,59 @@ The com :::moniker-end +:::moniker range=">= aspnetcore-11.0" + +## Control viewport scroll position behavior when items are dynamically added + + + +Assign a `VirtualizeAnchorMode` value to the `AnchorMode` parameter to control how the viewport behaves at list edges when items are dynamically added: + +* `None`: No edge pinning. The viewport stays at the current scroll position regardless of item changes. +* `Beginning`: Pins the viewport to the beginning of the list. When the user is at a scroll position near the top of the list and new items arrive at the beginning, the viewport stays at the top showing the newest items. For example, this pinning behavior is useful for a news feed user experience. +* `End`: Pins the viewport to the end of the list. When the user is at a scroll position near the bottom of the list and new items arrive at the end, the viewport auto-scrolls to show them. If the user has scrolled away, auto-scroll disengages until they return to the bottom. For example, this pinning behavior is useful for a chat or logging user experience. + +The following example pins the viewport to the beginning of a virtualized flight list: + +```razor +
+ + + +
+``` + +Modes can be combined. For example, assigning `Beginning | End` pins both edges. Combining `None` with other modes is supported but doesn't change the combined value. + +`Virtualize.ItemComparer` gets or sets a comparer used to detect whether items were prepended or appended when using class-typed items with an (for more information, see the [Item provider delegate](#item-provider-delegate) section). + +The comparer determines if the first loaded item changed between provider calls, which indicates items were inserted at the top. For records, the default comparer's value-equality behavior (`EqualityComparer.Default`) works automatically. For an in-memory assignment, an `ItemComparer` comparer isn't required because the component can detect prepends automatically. In cases where non-primative objects are virtualized and the framework can't detect if an item is prepended or appended, assign an to the `Virtualize` component: + + + +```razor + + ... + + +@code { + private static readonly IEqualityComparer itemComparer = + EqualityComparer.Create((a, b) => + a.Index == b.Index, item => item.Index); + + private async ValueTask> LoadFlights( + ItemsProviderRequest request) + { + ... + } +} +``` + +:::moniker-end + ## State changes When making changes to items rendered by the component, call to enqueue re-evaluation and rerendering of the component. For more information, see . diff --git a/aspnetcore/blazor/debug.md b/aspnetcore/blazor/debug.md index ebcca7914baa..95552664e401 100644 --- a/aspnetcore/blazor/debug.md +++ b/aspnetcore/blazor/debug.md @@ -164,7 +164,27 @@ Blazor Server: [`Microsoft.AspNetCore.Components.WebAssembly.Server`](https://ww :::moniker-end -Standalone Blazor WebAssembly: [`Microsoft.AspNetCore.Components.WebAssembly.DevServer`](https://www.nuget.org/packages/Microsoft.AspNetCore.Components.WebAssembly.DevServer): Development server for use when building Blazor apps. Calls internally to add middleware for debugging Blazor WebAssembly apps inside Chromium developer tools. + + +Standalone Blazor WebAssembly: [`Microsoft.AspNetCore.Components.WebAssembly.DevServer`](https://www.nuget.org/packages/Microsoft.AspNetCore.Components.WebAssembly.DevServer): Development server for use when building Blazor WebAssembly apps. Calls internally to add middleware for debugging Blazor WebAssembly apps inside Chromium developer tools. + + :::moniker range="< aspnetcore-8.0" diff --git a/aspnetcore/blazor/security/webassembly/standalone-with-identity/index.md b/aspnetcore/blazor/security/webassembly/standalone-with-identity/index.md index 4ed1294a446a..89ec24c0df0e 100644 --- a/aspnetcore/blazor/security/webassembly/standalone-with-identity/index.md +++ b/aspnetcore/blazor/security/webassembly/standalone-with-identity/index.md @@ -181,11 +181,34 @@ A standalone Blazor WebAssembly frontend app demonstrates user authentication an The app uses the following NuGet packages: + + * [`Microsoft.AspNetCore.Components.WebAssembly.Authentication`](https://www.nuget.org/packages/Microsoft.AspNetCore.Components.WebAssembly.Authentication) * [`Microsoft.Extensions.Http`](https://www.nuget.org/packages/Microsoft.Extensions.Http) * [`Microsoft.AspNetCore.Components.WebAssembly`](https://www.nuget.org/packages/Microsoft.AspNetCore.Components.WebAssembly) * [`Microsoft.AspNetCore.Components.WebAssembly.DevServer`](https://www.nuget.org/packages/Microsoft.AspNetCore.Components.WebAssembly.DevServer) + + ### Sample app code The `Models` folder contains the app's models: diff --git a/aspnetcore/blazor/state-management/server.md b/aspnetcore/blazor/state-management/server.md index f4f3eddceba1..e89e31547121 100644 --- a/aspnetcore/blazor/state-management/server.md +++ b/aspnetcore/blazor/state-management/server.md @@ -156,6 +156,8 @@ services.AddRazorComponents() In the preceding example, the `{CONNECTION STRING}` placeholder represents the Redis cache connection string, which should be provided using a secure approach, such as the [Secret Manager](xref:security/app-secrets#secret-manager) tool in the `Development` environment or [Azure Key Vault](/azure/key-vault/) with [Azure Managed Identities](/entra/identity/managed-identities-azure-resources/overview) for Azure-deployed apps in any environment. +For more information on the [`[PersistentState]` attribute](xref:Microsoft.AspNetCore.Components.PersistentStateAttribute), see . + ## Pause and resume circuits Pause and resume circuits to implement custom policies that improve the scalability of an app. @@ -164,8 +166,8 @@ Pausing a circuit stores details about the circuit in client-side browser storag From a JavaScript event handler: -* Call `Blazor.pauseCircuit` to pause a circuit. -* Call `Blazor.resumeCircuit` to resume a circuit. +* Call `Blazor.pauseCircuit()` to pause a circuit. +* Call `Blazor.resumeCircuit()` to resume a circuit. The following example assumes that a circuit isn't required for an app that isn't visible: @@ -181,6 +183,230 @@ window.addEventListener('visibilitychange', () => { :::moniker-end +:::moniker range=">= aspnetcore-11.0" + +## Server-triggered circuit pause + +A server-side Blazor app that adopts the Interactive Server render mode can implement server-triggered circuit pause, which allows the app to gracefully pause client circuits, preserving client state for seamless reconnection. + +This feature is useful in the following scenarios: + +* Planned shutdowns and deployments. +* Instance draining. +* App maintenance windows. + +`Circuit.RequestCircuitPauseAsync(CancellationToken)` is used to request that the connected client begin the graceful circuit-pause flow. The `CancellationToken` cancels the request before it is accepted by the framework. The method returns `true` if the request was accepted and the client was asked to begin pausing. + + + +:::moniker-end + :::moniker range="< aspnetcore-10.0" ## Persist state across circuits @@ -213,7 +439,7 @@ To persist temporary data between HTTP requests during static server-side render `TempData`: * Is available when is called in the app's `Program` file. -* Is provided as a cascading value with the [`[CascadingParameter]` attribute](xref:blazor/components/cascading-values-and-parameters#cascadingparameter-attribute). +* Is provided as a cascading value with the [`[CascadingParameter]` attribute](xref:blazor/components/cascading-values-and-parameters#cascadingparameter-attribute) or the `[SupplyParameterFromTempData]` parameter attribute. * Is accessed by key (string). * Supports primitives, , , enums, and collections (arrays, , ). * Stores `object?` values, requiring runtime casting (example: `var message = TempData["Message"] as string`). IntelliSense and type checking aren't supported. @@ -224,6 +450,16 @@ To persist temporary data between HTTP requests during static server-side render public ITempData? TempData { get; set; } ``` +When supplied to a parameter for simple read/write of a single value, use the `[SupplyParameterFromTempData]` attribute without or with a key (string): + +```csharp +[SupplyParameterFromTempData] +public string? Message { get; set; } + +[SupplyParameterFromTempData(Name = "flash_message")] +public string? FlashMessage { get; set; } +``` + The `ITempData` interface provides the following methods for controlling value lifecycle: * `Get`: Gets the value associated with the specified key and schedules the data for deletion. @@ -286,15 +522,16 @@ Browsers enforce a 4 KB cookie size limit. `TempData` automatically uses @message

-
+ + @@ -310,10 +547,10 @@ In the following example, a form displays a message that's retained in `TempData message = TempData?.Get("Message") as string ?? "No message"; } - private void HandleSubmit() + private void Submit() { TempData!["Message"] = "Form submitted successfully!"; - NavigationManager.NavigateTo("/tempdata-example", forceLoad: true); + NavigationManager.NavigateTo("/tempdata-example-1", forceLoad: true); } } ``` @@ -346,6 +583,33 @@ protected override void OnInitialized() } ``` +Similar to the preceding example but when only simple read/write of a single value is required, the following example uses the `[SupplyParameterFromTempData]` attribute. + +`Pages/TempDataExample2.razor`: + +```razor +@page "/tempdata-example-2" +@inject NavigationManager NavigationManager + +

@Message

+ +
+ + + + +@code { + [SupplyParameterFromTempData] + public string? Message { get; set; } + + private void Submit() + { + Message = "Form submitted successfully!"; + NavigationManager.NavigateTo("/tempdata-example-2", forceLoad: true); + } +} +``` + ### Permanent data persistence :::moniker-end diff --git a/aspnetcore/blazor/tooling.md b/aspnetcore/blazor/tooling.md index 0f07d69442d7..131dbc699bad 100644 --- a/aspnetcore/blazor/tooling.md +++ b/aspnetcore/blazor/tooling.md @@ -494,9 +494,25 @@ For more information, see the following resources: ## Blazor project templates and template options +:::moniker range=">= aspnetcore-11.0" + +* Blazor Web App project template: `blazor` +* Standalone Blazor WebAssembly app project template: `blazorwasm` +* [Service defaults library for Blazor WebAssembly apps](#service-defaults-library-for-blazor-webassembly-apps): `blazor-wasm-servicedefaults` + +> [!NOTE] +> The "Hosted" Blazor WebAssembly project template option isn't available in .NET 8 or later. To create a hosted Blazor WebAssembly app, a **Framework** option earlier than .NET 8 must be selected with the **ASP.NET Core Hosted** checkbox. However, we recommend a Blazor Web App for all new Blazor development in .NET 8 or later. For more information, see the following resources: +> +> * +> * +> * +> * + +:::moniker-end + The Blazor framework provides project templates for creating new apps. The templates are used to create new Blazor projects and solutions regardless of the tooling that you select for Blazor development (Visual Studio, Visual Studio Code, or the [.NET command-line interface (CLI)](/dotnet/core/tools/)): -:::moniker range=">= aspnetcore-8.0" +:::moniker range=">= aspnetcore-8.0 < aspnetcore-11.0" * Blazor Web App project template: `blazor` * Standalone Blazor WebAssembly app project template: `blazorwasm` @@ -604,6 +620,32 @@ For more information on template options, see the following resources: :::moniker-end +:::moniker range=">= aspnetcore-11.0" + +## Service defaults library for Blazor WebAssembly apps + +The `blazor-wasm-servicedefaults` project template creates a service defaults library for Blazor WebAssembly apps with .NET Aspire integration. + +The template features: + +* [OpenTelemetry](https://github.com/open-telemetry/opentelemetry-dotnet) support: Logging, metrics, and tracing with standard OTLP exporter. +* Service discovery integration via the [`Microsoft.Extensions.ServiceDiscovery` NuGet package](https://www.nuget.org/packages/Microsoft.Extensions.ServiceDiscovery). +* HTTP resilience using the standard resilience handler via the [`Microsoft.Extensions.Http.Resilience` NuGet package](https://www.nuget.org/packages/Microsoft.Extensions.Http.Resilience). + +Example template usage with an output name of `BlazorSample.ServiceDefaults`: + +```dotnetcli +dotnet new blazor-wasm-servicedefaults -o BlazorSample.ServiceDefaults +``` + +Reference the library from the Blazor WebAssembly client and make the following call in the app's `Program` file: + +```csharp +builder.AddBlazorClientServiceDefaults(); +``` + +:::moniker-end + ## Additional resources :::moniker range=">= aspnetcore-6.0" diff --git a/aspnetcore/mobile/native-mobile-backend.md b/aspnetcore/mobile/native-mobile-backend.md index 79575952b28b..a6b32cdbdf02 100644 --- a/aspnetcore/mobile/native-mobile-backend.md +++ b/aspnetcore/mobile/native-mobile-backend.md @@ -3,7 +3,7 @@ title: Create backend services for native mobile apps with ASP.NET Core author: wadepickett description: Learn how to create backend services using ASP.NET Core MVC to support native mobile apps. ms.author: wpickett -ms.date: 02/10/2025 +ms.date: 05/12/2026 uid: mobile/native-mobile-backend --- # Create backend services for native mobile apps with ASP.NET Core @@ -12,7 +12,7 @@ By [James Montemagno](https://twitter.com/JamesMontemagno) Mobile apps can communicate with ASP.NET Core backend services. For instructions on connecting local web services from iOS simulators and Android emulators, see [Connect to local web services from Android emulators and iOS simulators](/dotnet/maui/data-cloud/local-web-services). -[View or download sample backend services code](https://github.com/dotnet/maui-samples/tree/main/9.0/WebServices/TodoREST) +[View or download sample backend services code](https://github.com/dotnet/maui-samples/tree/main/10.0/WebServices/TodoREST) ## The sample native mobile app @@ -22,7 +22,7 @@ This tutorial demonstrates how to create backend services using ASP.NET Core to ### Features -The [TodoREST app](https://github.com/dotnet/maui-samples/tree/main/9.0/WebServices/TodoREST) supports listing, adding, deleting, and updating todo items. Each item has an ID, a name, notes, and a property indicating whether it's been done yet. +The [TodoREST app](https://github.com/dotnet/maui-samples/tree/main/10.0/WebServices/TodoREST) supports listing, adding, deleting, and updating todo items. Each item has an ID, a name, notes, and a property indicating whether it's been done yet. In the previous example, The main view of the items lists each item's name and indicates if it's done with a checkmark. @@ -34,13 +34,13 @@ Tapping an item on the main page navigates to an edit page where the item's name ![Edit item dialog](native-mobile-backend/_static/todo-android-edit-item.png) -To test it out yourself against the ASP.NET Core app created in the next section, if you host it online, update the app's [`RestUrl`](https://github.com/dotnet/maui-samples/blob/52607dc4ebf19a51ce59694b57e704b67600f69b/9.0/WebServices/TodoREST/TodoREST/Constants.cs#L6) constant. Otherwise, the app will communicate with the ASP.NET Core app that's hosted locally on your machine. +To test it out yourself against the ASP.NET Core app created in the next section, if you host it online, update the app's [`RestUrl`](https://github.com/dotnet/maui-samples/blob/main/10.0/WebServices/TodoREST/TodoREST/Constants.cs#L6) constant. Otherwise, the app will communicate with the ASP.NET Core app that's hosted locally on your machine. Android emulators don't run on the local machine and use a loopback IP (10.0.2.2) to communicate with the local machine. Use .NET MAUI's [DeviceInfo](/dotnet/maui/platform-integration/device/information) class to detect the operating system the app is running on to use the correct URL. -Navigate to the [`TodoREST`](https://github.com/dotnet/maui-samples/tree/main/9.0/WebServices/TodoREST) project and open the [`Constants.cs`](https://github.com/dotnet/maui-samples/blob/main/9.0/WebServices/TodoREST/TodoREST/Constants.cs) file. The `Constants.cs` file contains the following configuration. +Navigate to the [`TodoREST`](https://github.com/dotnet/maui-samples/tree/main/10.0/WebServices/TodoREST) project and open the [`Constants.cs`](https://github.com/dotnet/maui-samples/blob/main/10.0/WebServices/TodoREST/TodoREST/Constants.cs) file. The `Constants.cs` file contains the following configuration. -:::code language="csharp" source="~/../maui-samples/9.0/WebServices/TodoREST/TodoREST/Constants.cs" highlight="10"::: +:::code language="csharp" source="~/../maui-samples/10.0/WebServices/TodoREST/TodoREST/Constants.cs" highlight="10"::: You can optionally deploy the web service to a cloud service such as Azure and update the `RestUrl`. @@ -57,27 +57,27 @@ The app should respond to all requests made over HTTPS to port 5001. Add a model class to represent todo items. Mark required fields with the `[Required]` attribute: -:::code language="csharp" source="~/../maui-samples/9.0/WebServices/TodoREST/TodoAPI/Models/TodoItem.cs"::: +:::code language="csharp" source="~/../maui-samples/10.0/WebServices/TodoREST/TodoAPI/Models/TodoItem.cs"::: API methods require defining to work with data. Use the same `ITodoRepository` interface the sample uses: -:::code language="csharp" source="~/../maui-samples/9.0/WebServices/TodoREST/TodoAPI/Interfaces/ITodoRepository.cs"::: +:::code language="csharp" source="~/../maui-samples/10.0/WebServices/TodoREST/TodoAPI/Interfaces/ITodoRepository.cs"::: For this sample, the repository implementation just uses a private collection of items: -:::code language="csharp" source="~/../maui-samples/9.0/WebServices/TodoREST/TodoAPI/Services/TodoRepository.cs"::: +:::code language="csharp" source="~/../maui-samples/10.0/WebServices/TodoREST/TodoAPI/Services/TodoRepository.cs"::: Configure the implementation in `Program.cs`: -:::code language="csharp" source="~/../maui-samples/9.0/WebServices/TodoREST/TodoAPI/Program.cs" highlight="5"::: +:::code language="csharp" source="~/../maui-samples/10.0/WebServices/TodoREST/TodoAPI/Program.cs" highlight="5"::: ## Creating the Controller -Add a new controller to the project, [TodoItemsController](https://github.com/dotnet/maui-samples/blob/main/9.0/WebServices/TodoREST/TodoAPI/Controllers/TodoItemsController.cs). It should inherit from . Add a `Route` attribute to indicate that the controller handles requests made to paths starting with `api/todoitems`. The `[controller]` token in the route is replaced by the name of the controller (omitting the `Controller` suffix), and is especially helpful for global routes. Learn more about [routing](../fundamentals/routing.md). +Add a new controller to the project, [TodoItemsController](https://github.com/dotnet/maui-samples/blob/main/10.0/WebServices/TodoREST/TodoAPI/Controllers/TodoItemsController.cs). It should inherit from . Add a `Route` attribute to indicate that the controller handles requests made to paths starting with `api/todoitems`. The `[controller]` token in the route is replaced by the name of the controller (omitting the `Controller` suffix), and is especially helpful for global routes. Learn more about [routing](../fundamentals/routing.md). The controller requires an `ITodoRepository` to function; request an instance of this type through the controller's constructor. At runtime, this instance is provided using the framework's support for [dependency injection](../fundamentals/dependency-injection.md). -:::code language="csharp" source="~/../maui-samples/9.0/WebServices/TodoREST/TodoAPI/Controllers/TodoItemsController.cs" id="snippetDI"::: +:::code language="csharp" source="~/../maui-samples/10.0/WebServices/TodoREST/TodoAPI/Controllers/TodoItemsController.cs" id="snippetDI"::: This API supports four different HTTP verbs to perform CRUD (Create, Read, Update, Delete) operations on the data source. The simplest of these is the Read operation, which corresponds to an HTTP `GET` request. @@ -132,7 +132,7 @@ For more details on jq installation, see [jq](https://jqlang.github.io/jq/downlo Requesting a list of items is done with a GET request to the `List` method. The `[HttpGet]` attribute on the `List` method indicates that this action should only handle GET requests. The route for this action is the route specified on the controller. You don't necessarily need to use the action name as part of the route. You just need to ensure each action has a unique and unambiguous route. Routing attributes can be applied at both the controller and method levels to build up specific routes. -:::code language="csharp" source="~/../maui-samples/9.0/WebServices/TodoREST/TodoAPI/Controllers/TodoItemsController.cs" id="snippet"::: +:::code language="csharp" source="~/../maui-samples/10.0/WebServices/TodoREST/TodoAPI/Controllers/TodoItemsController.cs" id="snippet"::: # [macOS](#tab/macos) @@ -195,11 +195,11 @@ By convention, creating new data items is mapped to the HTTP `POST` verb. The `C Inside the method, the item is checked for validity and prior existence in the data store, and if no issues occur, it's added using the repository. Checking `ModelState.IsValid` performs [model validation](../mvc/models/validation.md), and should be done in every API method that accepts user input. -:::code language="csharp" source="~/../maui-samples/9.0/WebServices/TodoREST/TodoAPI/Controllers/TodoItemsController.cs" id="snippetCreate"::: +:::code language="csharp" source="~/../maui-samples/10.0/WebServices/TodoREST/TodoAPI/Controllers/TodoItemsController.cs" id="snippetCreate"::: The sample uses an `enum` containing error codes that are passed to the mobile client: -:::code language="csharp" source="~/../maui-samples/9.0/WebServices/TodoREST/TodoAPI/Controllers/TodoItemsController.cs" id="snippetErrorCode"::: +:::code language="csharp" source="~/../maui-samples/10.0/WebServices/TodoREST/TodoAPI/Controllers/TodoItemsController.cs" id="snippetErrorCode"::: In the terminal, test adding new items by calling the following curl command using the `POST` verb and providing the new object in JSON format in the Body of the request. @@ -242,7 +242,7 @@ The method returns the newly created item in the response. Modifying records is achieved using HTTP `PUT` requests. Other than this change, the `Edit` method is almost identical to `Create`. If the record isn't found, the `Edit` action returns a `NotFound` (404) response. -:::code language="csharp" source="~/../maui-samples/9.0/WebServices/TodoREST/TodoAPI/Controllers/TodoItemsController.cs" id="snippetEdit"::: +:::code language="csharp" source="~/../maui-samples/10.0/WebServices/TodoREST/TodoAPI/Controllers/TodoItemsController.cs" id="snippetEdit"::: To test with curl, change the verb to `PUT`. Specify the updated object data in the Body of the request. @@ -279,7 +279,7 @@ This method returns a `NoContent` (204) response when successful, for consistenc Deleting records is accomplished by making `DELETE` requests to the service, and passing the ID of the item to be deleted. As with updates, requests for items that don't exist receive `NotFound` responses. Otherwise, a successful request returns a `NoContent` (204) response. -:::code language="csharp" source="~/../maui-samples/9.0/WebServices/TodoREST/TodoAPI/Controllers/TodoItemsController.cs" id="snippetDelete"::: +:::code language="csharp" source="~/../maui-samples/10.0/WebServices/TodoREST/TodoAPI/Controllers/TodoItemsController.cs" id="snippetDelete"::: Test with curl by changing the HTTP verb to `DELETE` and appending the ID of the data object to delete at the end of the URL. Nothing is required in the Body of the request. diff --git a/aspnetcore/release-notes/aspnetcore-11.md b/aspnetcore/release-notes/aspnetcore-11.md index c3ffe60376a4..226c0253542f 100644 --- a/aspnetcore/release-notes/aspnetcore-11.md +++ b/aspnetcore/release-notes/aspnetcore-11.md @@ -5,7 +5,7 @@ author: wadepickett description: Learn about the new features in ASP.NET Core in .NET 11. ms.author: wpickett ms.custom: mvc -ms.date: 03/13/2026 +ms.date: 05/12/2026 uid: aspnetcore-11 --- # What's new in ASP.NET Core in .NET 11 @@ -34,6 +34,8 @@ This section describes new features for SignalR. This section describes new features for Minimal APIs. +[!INCLUDE[](~/release-notes/aspnetcore-11/includes/endpoint-filters-binding-failures-preview4.md)] + ## OpenAPI This section describes new features for OpenAPI. @@ -42,6 +44,10 @@ This section describes new features for OpenAPI. [!INCLUDE[](~/release-notes/aspnetcore-11/includes/openapi-3-2-support-preview2.md)] +[!INCLUDE[](~/release-notes/aspnetcore-11/includes/http-query-openapi-preview4.md)] + +[!INCLUDE[](~/release-notes/aspnetcore-11/includes/file-result-openapi-preview4.md)] + ## Authentication and authorization This section describes new features for authentication and authorization. @@ -66,6 +72,14 @@ This section describes miscellaneous new features in .NET 11. [!INCLUDE[](~/release-notes/aspnetcore-11/includes/http3-early-request-processing-preview-3.md)] +[!INCLUDE[](~/release-notes/aspnetcore-11/includes/mcp-server-template-preview4.md)] + +[!INCLUDE[](~/release-notes/aspnetcore-11/includes/tls-handshake-observability-preview4.md)] + +[!INCLUDE[](~/release-notes/aspnetcore-11/includes/response-compression-vary-header-preview4.md)] + +[!INCLUDE[](~/release-notes/aspnetcore-11/includes/runtime-async-shared-framework-preview4.md)] + ## Breaking changes Use the articles in [Breaking changes in .NET](/dotnet/core/compatibility/breaking-changes) to find breaking changes that might apply when upgrading an app to a newer version of .NET. diff --git a/aspnetcore/release-notes/aspnetcore-11/includes/blazor.md b/aspnetcore/release-notes/aspnetcore-11/includes/blazor.md index 1bc4f75667c5..d1818c71cac2 100644 --- a/aspnetcore/release-notes/aspnetcore-11/includes/blazor.md +++ b/aspnetcore/release-notes/aspnetcore-11/includes/blazor.md @@ -112,14 +112,74 @@ TempData is available when . +When supplied to a parameter for simple read/write of a single value, use the `[SupplyParameterFromTempData]` attribute: + +```csharp +[SupplyParameterFromTempData] +public string? Message { get; set; } +``` + +For more information, see . ### New Blazor Web Worker template (`blazorwebworker`) Blazor WebAssembly apps can perform heavy computing on the client, but doing so on the UI thread interferes with UI rendering and negatively affects the user experience. In .NET 10, we added an article with a sample app to make offloading heavy work from the UI thread to a Web Worker easier. For .NET 11, we've added the Blazor Web Worker project template (`blazorwebworker`), which provides infrastructure for running .NET code in a Web Worker in Blazor WebAssembly apps. For more information, see . -### `Virtualize` adapts to variable-height items at runtime +### Virtualization enhancements + +* The component no longer assumes every item has the same height. The component now adapts to measured item sizes at runtime, which reduces incorrect spacing and scrolling when item heights vary. These updates include an update to the default value of , which was `3` in .NET 10 or earlier and now changes to `15` in .NET 11 or later. The change in default value increases the precision of average item height calculations. + + For more information, see the [*Item size* section](xref:blazor/components/virtualization?view=aspnetcore-11.0#item-size) and [*Overscan count* section](xref:blazor/components/virtualization?view=aspnetcore-11.0#overscan-count) of the *Virtualization* article. + +* Use the new `AnchorMode` parameter to control how the viewport behaves at list edges when items are dynamically added: + + * `None`: No edge pinning. The viewport stays at the current scroll position regardless of item changes. + * `Beginning`: Pins the viewport to the beginning of the list. For example, this pinning behavior is useful for a news feed user experience. + * `End`: Pins the viewport to the end of the list. For example, this pinning behavior is useful for a chat or logging user experience. + + In the following example, the virtualized content is pinned to the beginning of the list: + + ```razor + + ... + + ``` + + For more information, see . + +### New service defaults library project template for Blazor WebAssembly apps + +The `blazor-wasm-servicedefaults` project template creates a service defaults library for Blazor WebAssembly apps with .NET Aspire integration. For more information, see . + +### New development server for Blazor WebAssembly apps -The component no longer assumes every item has the same height. The component now adapts to measured item sizes at runtime, which reduces incorrect spacing and scrolling when item heights vary. These updates include an update to the default value of , which was `3` in .NET 10 or earlier and now changes to `15` in .NET 11 or later. The change in default value increases the precision of average item height calculations. + + +`Microsoft.AspNetCore.Components.Gateway` is a lightweight ASP.NET Core host that replaces [`Microsoft.AspNetCore.Components.WebAssembly.DevServer`](https://www.nuget.org/packages/Microsoft.AspNetCore.Components.WebAssembly.DevServer) for serving standalone Blazor WebAssembly applications during development and production. + +For more information, see [[Blazor] Replace DevServer with BlazorGateway for standalone WASM apps (`dotnet/aspnetcore` #65982)](https://github.com/dotnet/aspnetcore/pull/65982). + +### Server-triggered circuit pause + +*This feature applies to server-side Blazor apps.* + +Blazor already supports graceful circuit pause and resume with [`Blazor.pauseCircuit()` and `Blazor.resumeCircuit()`](xref:blazor/state-management/server#pause-and-resume-circuits). .NET 11 introduces a symmetric server-side pause and resume capability, where the server can request that connected clients begin the graceful circuit-pause flow. + +`Circuit.RequestCircuitPauseAsync(CancellationToken)` is used to request that the connected client begin the graceful circuit-pause flow. The `CancellationToken` cancels the request before it is accepted by the framework. The method returns `true` if the request was accepted and the client was asked to begin pausing. + +This feature is useful in the following scenarios: + +* Planned shutdowns and deployments. +* Instance draining. +* App maintenance windows. + +For more information, see . + + diff --git a/aspnetcore/release-notes/aspnetcore-11/includes/endpoint-filters-binding-failures-preview4.md b/aspnetcore/release-notes/aspnetcore-11/includes/endpoint-filters-binding-failures-preview4.md new file mode 100644 index 000000000000..c2ffb3115d56 --- /dev/null +++ b/aspnetcore/release-notes/aspnetcore-11/includes/endpoint-filters-binding-failures-preview4.md @@ -0,0 +1,7 @@ +### Endpoint filters observe parameter-binding failures + +When a minimal API endpoint has any filters or filter factories configured, the filter pipeline now runs even if parameter binding fails. Filters can read `HttpContext.Response.StatusCode == 400` and substitute their own response body. + +In the `Development` environment, set `RouteHandlerOptions.ThrowOnBadRequest = false` so the framework returns a 400 that the filter can observe instead of throwing to the developer exception page. This is already the default in non-`Development` environments. + +Thank you [@marcominerva](https://github.com/marcominerva) for this contribution! diff --git a/aspnetcore/release-notes/aspnetcore-11/includes/file-result-openapi-preview4.md b/aspnetcore/release-notes/aspnetcore-11/includes/file-result-openapi-preview4.md new file mode 100644 index 000000000000..471b6bccdb70 --- /dev/null +++ b/aspnetcore/release-notes/aspnetcore-11/includes/file-result-openapi-preview4.md @@ -0,0 +1,5 @@ +### File stream result types appear in OpenAPI documents + +`FileStreamResult`, `FileContentHttpResult`, and `FileStreamHttpResult` are now described as binary string schemas in generated OpenAPI documents, so clients see accurate response shapes for endpoints that stream files. Annotate the endpoint with `.Produces(contentType: "application/pdf")` (or the equivalent `FileStreamHttpResult`/`FileStreamResult` type) so OpenAPI sees the result type and emits the binary schema. + +Thank you [@marcominerva](https://github.com/marcominerva) for this contribution! diff --git a/aspnetcore/release-notes/aspnetcore-11/includes/http-query-openapi-preview4.md b/aspnetcore/release-notes/aspnetcore-11/includes/http-query-openapi-preview4.md new file mode 100644 index 000000000000..b30ab44b836a --- /dev/null +++ b/aspnetcore/release-notes/aspnetcore-11/includes/http-query-openapi-preview4.md @@ -0,0 +1,55 @@ +### HTTP QUERY in generated OpenAPI documents + +OpenAPI document generation now recognizes [HTTP QUERY](https://datatracker.ietf.org/doc/draft-ietf-httpbis-safe-method-w-body/) as a known operation type. QUERY is a proposed safe, idempotent method that lets clients send a request body when describing a search, useful when a query is too large or too structured to fit in a URL. Routing already accepts arbitrary verb strings via `MapMethods`, and OpenAPI 3.2 adds a [`query` field to the Path Item Object](https://spec.openapis.org/oas/v3.2.0.html#fixed-fields-6) so this can be described in the OpenAPI document. + +Note that `query` is only valid in an OpenAPI 3.2 document, so set the `OpenApiVersion` in the `OpenApiOptions`. In earlier OpenAPI versions, the `query` operation is generated within an `x-oai-additionalOperations` specification extension in the Path Item Object. + +```csharp +using Microsoft.OpenApi; + +var builder = WebApplication.CreateBuilder(args); + +builder.Services.AddOpenApi(options => +{ + options.OpenApiVersion = OpenApiSpecVersion.OpenApi3_2; +}); + +var app = builder.Build(); + +app.MapOpenApi(); + +app.MapMethods("/search", ["QUERY"], (SearchRequest request) => + SearchService.Run(request)); + +app.Run(); +``` + +In an OpenAPI 3.2 document, the QUERY operation is described inline as a sibling of `get`, `post`, and other standard operations: + +```json +"paths": { + "/search": { + "query": { + "requestBody": { ... }, + "responses": { "200": { ... } } + } + } +} +``` + +In OpenAPI 3.0 and 3.1 documents, the same operation is represented under the `x-oai-additionalOperations` extension on the Path Item: + +```json +"paths": { + "/search": { + "x-oai-additionalOperations": { + "QUERY": { + "requestBody": { ... }, + "responses": { "200": { ... } } + } + } + } +} +``` + +Thank you [@kilifu](https://github.com/kilifu) for this contribution! diff --git a/aspnetcore/release-notes/aspnetcore-11/includes/mcp-server-template-preview4.md b/aspnetcore/release-notes/aspnetcore-11/includes/mcp-server-template-preview4.md new file mode 100644 index 000000000000..1f5ca039aba0 --- /dev/null +++ b/aspnetcore/release-notes/aspnetcore-11/includes/mcp-server-template-preview4.md @@ -0,0 +1,9 @@ +### MCP Server template ships with the .NET SDK + +The `mcpserver` project template, previously available only by installing `Microsoft.McpServer.ProjectTemplates`, now ships as a bundled template in the .NET SDK: + +```dotnetcli +dotnet new mcpserver -o MyMcpServer +``` + +Moving the template into ASP.NET Core makes it discoverable from `dotnet new list` without a separate install step, and aligns its servicing with the rest of the web stack. diff --git a/aspnetcore/release-notes/aspnetcore-11/includes/response-compression-vary-header-preview4.md b/aspnetcore/release-notes/aspnetcore-11/includes/response-compression-vary-header-preview4.md new file mode 100644 index 000000000000..1ba06613efc1 --- /dev/null +++ b/aspnetcore/release-notes/aspnetcore-11/includes/response-compression-vary-header-preview4.md @@ -0,0 +1,5 @@ +### Response compression always emits `Vary: Accept-Encoding` + +The response-compression middleware now adds `Vary: Accept-Encoding` to every response when compression is enabled, even when the response itself isn't compressed. This prevents shared caches and CDNs from serving a compressed payload to a client that didn't ask for one (or vice versa). + +Thank you [@pedrobsaila](https://github.com/pedrobsaila) for this contribution! diff --git a/aspnetcore/release-notes/aspnetcore-11/includes/runtime-async-shared-framework-preview4.md b/aspnetcore/release-notes/aspnetcore-11/includes/runtime-async-shared-framework-preview4.md new file mode 100644 index 000000000000..ae634777a223 --- /dev/null +++ b/aspnetcore/release-notes/aspnetcore-11/includes/runtime-async-shared-framework-preview4.md @@ -0,0 +1,7 @@ +### Runtime-async enabled for shared framework libraries + +ASP.NET Core's shared-framework-only libraries are now compiled with the `runtime-async` feature on `net11.0+`. Runtime-async lets the runtime, rather than the C# compiler, generate the state machine for `async`/`await`, which can reduce per-await allocations and improve diagnostics. This is an internal codegen change with no public API impact — apps targeting `net11.0` automatically benefit when they call into the affected ASP.NET Core libraries. + +Libraries that ship as both shared-framework members and standalone NuGet packages are excluded, because runtime-async is incompatible with WebAssembly and would otherwise break Wasm consumers of those packages. + +Because runtime-async changes how `async`/`await` is generated for a large portion of the ASP.NET Core stack, try your apps against this preview and [file an issue](https://github.com/dotnet/aspnetcore/issues/new/choose) if you hit unexpected behavior, particularly around exception stacks, `ExecutionContext`/`AsyncLocal` flow, or anything that looks like a regression from .NET 10. diff --git a/aspnetcore/release-notes/aspnetcore-11/includes/tls-handshake-observability-preview4.md b/aspnetcore/release-notes/aspnetcore-11/includes/tls-handshake-observability-preview4.md new file mode 100644 index 000000000000..62f89e6be3eb --- /dev/null +++ b/aspnetcore/release-notes/aspnetcore-11/includes/tls-handshake-observability-preview4.md @@ -0,0 +1,35 @@ +### TLS handshake observability in Kestrel + +Two related changes make it easier to diagnose and customize TLS connections in Kestrel. + +`ITlsHandshakeFeature` now exposes an `Exception` property containing the exception thrown during a failed TLS handshake, so middleware and logging can record why a connection failed instead of seeing a bare `IOException` further up the stack. The feature continues to work after the handshake fails — Kestrel snapshots the relevant fields off the underlying `SslStream` before it is disposed. + +The `TlsClientHelloBytesCallback` option on `HttpsConnectionAdapterOptions` was reworked as a connection middleware. The previous callback shape is now obsolete; configure ClientHello inspection via the new `ListenOptions.UseTlsClientHelloListener` extension instead. The example below uses both features together — connection middleware reads `ITlsHandshakeFeature.Exception` after the handshake, and `UseTlsClientHelloListener` inspects the ClientHello before TLS: + +```csharp +var builder = WebApplication.CreateBuilder(args); + +builder.WebHost.ConfigureKestrel(options => +{ + options.ListenAnyIP(5001, listenOptions => + { + listenOptions.Use(next => async context => + { + await next(context); + + var tlsHandshakeFeature = context.Features.Get(); + if (tlsHandshakeFeature?.Exception is { } ex) + { + Console.WriteLine($"[TLS Handshake Failed] ConnectionId={context.ConnectionId}, Exception={ex.GetType().Name}: {ex.Message}"); + } + }); + + // UseTlsClientHelloListener must be called before UseHttps() + listenOptions.UseTlsClientHelloListener((connection, clientHelloBytes) => + { + Console.WriteLine($"TLS Client Hello received on {connection.ConnectionId}, {clientHelloBytes.Length} bytes"); + }); + listenOptions.UseHttps(); + }); +}); +``` diff --git a/aspnetcore/security/cors.md b/aspnetcore/security/cors.md index 12662e2a3ee8..71d4f6de436b 100644 --- a/aspnetcore/security/cors.md +++ b/aspnetcore/security/cors.md @@ -5,7 +5,7 @@ author: tdykstra description: Learn how CORS as a standard for allowing or rejecting cross-origin requests in an ASP.NET Core app. ms.author: tdykstra ms.custom: mvc -ms.date: 11/10/2025 +ms.date: 05/12/2026 uid: security/cors --- # Enable Cross-Origin Requests (CORS) in ASP.NET Core @@ -247,7 +247,7 @@ CORS Middleware declines a preflight request with the following request header b Access-Control-Request-Headers: Cache-Control, Content-Language ``` -The app returns a *200 OK* response but doesn't send the CORS headers back. Therefore, the browser doesn't attempt the cross-origin request. +The app returns a `204 No Content` response but doesn't send the CORS headers back. Therefore, the browser doesn't attempt the cross-origin request. ### Set the exposed response headers @@ -364,7 +364,7 @@ The preflight request uses the [HTTP OPTIONS](https://developer.mozilla.org/docs * [Access-Control-Request-Method](https://developer.mozilla.org/docs/Web/HTTP/Headers/Access-Control-Request-Method): The HTTP method that will be used for the actual request. * [Access-Control-Request-Headers](https://developer.mozilla.org/docs/Web/HTTP/Headers/Access-Control-Allow-Headers): A list of request headers that the app sets on the actual request. As stated earlier, this doesn't include headers that the browser sets, such as `User-Agent`. -If the preflight request is denied, the app returns a `200 OK` response but doesn't set the CORS headers. Therefore, the browser doesn't attempt the cross-origin request. For an example of a denied preflight request, see the [Test CORS](#testc6) section of this document. +If the preflight request is denied, the app returns a `204 No Content` response but doesn't set the CORS headers. Therefore, the browser doesn't attempt the cross-origin request. For an example of a denied preflight request, see the [Test CORS](#testc6) section of this document. Using the F12 tools, the console app shows an error similar to one of the following, depending on the browser: