From a6a21553065ef00280f45e9365abf97aa365bac2 Mon Sep 17 00:00:00 2001 From: danroth27 Date: Wed, 29 Apr 2026 16:50:18 -0700 Subject: [PATCH 01/15] [release-notes] ASP.NET Core in .NET 11 Preview 4 (initial) --- release-notes/11.0/preview/preview4/aspnetcore.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 release-notes/11.0/preview/preview4/aspnetcore.md diff --git a/release-notes/11.0/preview/preview4/aspnetcore.md b/release-notes/11.0/preview/preview4/aspnetcore.md new file mode 100644 index 00000000000..31e7d7ed614 --- /dev/null +++ b/release-notes/11.0/preview/preview4/aspnetcore.md @@ -0,0 +1,3 @@ +# ASP.NET Core in .NET 11 Preview 4 - Release Notes + +_Authored content pending — agent assigned._ From 975a3cd9e8f2dcb617bd845a8ff5797aea124126 Mon Sep 17 00:00:00 2001 From: danroth27 Date: Wed, 29 Apr 2026 17:11:33 -0700 Subject: [PATCH 02/15] [release-notes] Author aspnetcore.md content --- .../11.0/preview/preview4/aspnetcore.md | 184 +++++++++++++++++- 1 file changed, 183 insertions(+), 1 deletion(-) diff --git a/release-notes/11.0/preview/preview4/aspnetcore.md b/release-notes/11.0/preview/preview4/aspnetcore.md index 31e7d7ed614..c7301b4f198 100644 --- a/release-notes/11.0/preview/preview4/aspnetcore.md +++ b/release-notes/11.0/preview/preview4/aspnetcore.md @@ -1,3 +1,185 @@ # ASP.NET Core in .NET 11 Preview 4 - Release Notes -_Authored content pending — agent assigned._ +.NET 11 Preview 4 includes new ASP.NET Core features and improvements: + +- [Support for the HTTP QUERY method](#support-for-the-http-query-method) +- [SupplyParameterFromTempData for Blazor](#supplyparameterfromtempdata-for-blazor) +- [Server-initiated Blazor Server circuit pause](#server-initiated-blazor-server-circuit-pause) +- [Virtualize keeps the viewport stable when content above it changes](#virtualize-keeps-the-viewport-stable-when-content-above-it-changes) +- [Blazor WebAssembly service defaults template](#blazor-webassembly-service-defaults-template) +- [MCP Server template ships with the .NET SDK](#mcp-server-template-ships-with-the-net-sdk) +- [TLS handshake observability in Kestrel](#tls-handshake-observability-in-kestrel) +- [Response compression always emits `Vary: Accept-Encoding`](#response-compression-always-emits-vary-accept-encoding) +- [OpenAPI and minimal API improvements](#openapi-and-minimal-api-improvements) +- [Smaller Blazor WebAssembly publish output](#smaller-blazor-webassembly-publish-output) +- [Breaking changes](#breaking-changes) +- [Bug fixes](#bug-fixes) +- [Community contributors](#community-contributors) + +ASP.NET Core updates in .NET 11: + +- [What's new in ASP.NET Core in .NET 11](https://learn.microsoft.com/aspnet/core/release-notes/aspnetcore-11) + +## Support for the HTTP QUERY method + +ASP.NET Core now recognizes [HTTP QUERY](https://datatracker.ietf.org/doc/draft-ietf-httpbis-safe-method-w-body/) as a first-class HTTP method in routing, minimal APIs, and OpenAPI document generation ([dotnet/aspnetcore #65714](https://github.com/dotnet/aspnetcore/pull/65714)). 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. + +```csharp +var app = WebApplication.Create(); + +app.MapMethods("/search", ["QUERY"], (SearchRequest request) => + SearchService.Run(request)); + +app.Run(); +``` + +The method shows up in generated OpenAPI documents alongside `GET`, `POST`, and friends, so client generators and API explorers pick it up automatically. + +Thank you [@kilifu](https://github.com/kilifu) for this contribution! + +## `SupplyParameterFromTempData` for Blazor + +A new `[SupplyParameterFromTempData]` attribute reads and writes TempData values directly on a Blazor SSR component property, in the same style as `[SupplyParameterFromQuery]` and `[SupplyParameterFromForm]` ([dotnet/aspnetcore #65306](https://github.com/dotnet/aspnetcore/pull/65306)). + +```razor +@page "/account/manage" + + + +@code { + [SupplyParameterFromTempData] + public string? StatusMessage { get; set; } +} +``` + +If the property name doesn't match the TempData key you want, set `Name` on the attribute. Setting the property writes through to TempData so the next request can read it — handy for status messages that survive a redirect-after-post. + +The Blazor Identity project template was rewritten to use this attribute instead of the previous custom status-message cookie ([dotnet/aspnetcore #65752](https://github.com/dotnet/aspnetcore/pull/65752)). + +## Server-initiated Blazor Server circuit pause + +`Circuit.RequestCircuitPauseAsync` lets server-side code ask the connected Blazor client to begin the graceful circuit-pause flow ([dotnet/aspnetcore #66455](https://github.com/dotnet/aspnetcore/pull/66455)). Until now, pause was only triggered by client-side navigation; this gives operators a programmatic way to drain circuits during deployments or load-balancer rebalancing. + +```csharp +public class RebalanceService(CircuitRegistry circuits) +{ + public async Task DrainAsync(CancellationToken ct) + { + foreach (var circuit in circuits.GetActiveCircuits()) + { + await circuit.RequestCircuitPauseAsync(); + } + } +} +``` + +The method returns `true` when the client was successfully asked to begin pausing. Clients can defer the request via the optional `onPauseRequested` callback in `CircuitStartOptions`. + + + +## Virtualize keeps the viewport stable when content above it changes + +Preview 3 taught Blazor's `Virtualize` to handle items with different heights. Preview 4 builds on that with viewport stability: visible items stay in place when items above the viewport change height, and when items are prepended to the collection ([dotnet/aspnetcore #65951](https://github.com/dotnet/aspnetcore/pull/65951)). + +The component uses native CSS scroll anchoring where the browser supports it and falls back to manual scroll compensation otherwise (notably for `` layouts and Safari). Apps using `Virtualize` get this automatically — no API changes required. + +## Blazor WebAssembly service defaults template + +A new `blazor-wasm-servicedefaults` template scaffolds a service-defaults library for Blazor WebAssembly clients with .NET Aspire integration ([dotnet/aspnetcore #64807](https://github.com/dotnet/aspnetcore/pull/64807)). It mirrors the service-defaults pattern that Aspire already uses for server projects and `dotnet/maui` ships for MAUI clients. + +```bash +dotnet new blazor-wasm-servicedefaults -o MyBlazorApp.ServiceDefaults +``` + +The generated library wires up OpenTelemetry logging/metrics/tracing with the OTLP exporter, service discovery via `Microsoft.Extensions.ServiceDiscovery`, and standard HTTP resilience handlers from `Microsoft.Extensions.Http.Resilience`. Reference the project from your Blazor WebAssembly client and call the generated extension method during startup: + +```csharp +builder.AddBlazorClientServiceDefaults(); +``` + +## 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 ([dotnet/aspnetcore #66520](https://github.com/dotnet/aspnetcore/pull/66520)). + +```bash +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. + +## TLS handshake observability in Kestrel + +Two related changes make it easier to diagnose and customize TLS connections in Kestrel. + +`ITlsHandshakeFeature` now exposes 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 ([dotnet/aspnetcore #65807](https://github.com/dotnet/aspnetcore/pull/65807)). 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 ([dotnet/aspnetcore #65808](https://github.com/dotnet/aspnetcore/pull/65808)). The previous callback shape is now obsolete; configure ClientHello inspection via the connection-builder pipeline instead. + + + +## 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 ([dotnet/aspnetcore #55092](https://github.com/dotnet/aspnetcore/pull/55092)). 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! + +## OpenAPI and minimal API improvements + +Two changes round out the minimal API + OpenAPI experience this preview: + +- **File 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 ([dotnet/aspnetcore #64562](https://github.com/dotnet/aspnetcore/pull/64562)). Thank you [@marcominerva](https://github.com/marcominerva) for this contribution! +- **Endpoint filters can observe parameter-binding failures.** When a minimal API endpoint has filters configured and its delegate returns `ValueTask`, the filter pipeline now runs even if parameter binding fails. Filters can read the 400 response and substitute their own. Endpoints without filters continue to short-circuit with a 400 as before ([dotnet/aspnetcore #64539](https://github.com/dotnet/aspnetcore/pull/64539)). Thank you [@marcominerva](https://github.com/marcominerva) for this contribution! + +## Smaller Blazor WebAssembly publish output + +Two trimming changes shrink published Blazor WebAssembly apps that don't use OpenTelemetry or hot reload: + +- The `ComponentsMetrics` and `ComponentsActivitySource` types are now gated behind a `[FeatureSwitchDefinition]`, so the trimmer can drop the metrics and tracing call paths from `Renderer` and friends when `System.Diagnostics.Metrics.Meter.IsSupported` is `false` (the default for trimmed apps) ([dotnet/aspnetcore #65901](https://github.com/dotnet/aspnetcore/pull/65901)). +- `HotReloadManager` now exposes a feature-switched `IsSupported` property tied to `System.Reflection.Metadata.MetadataUpdater.IsSupported`, so the trimmer can eliminate hot-reload caches and metadata-update handler registrations across the renderer when published ([dotnet/aspnetcore #65903](https://github.com/dotnet/aspnetcore/pull/65903)). + +Apps that use OpenTelemetry or hot reload aren't affected — the feature switches default on in those configurations. + +## Breaking changes + +- **`%2F` is preserved in HTTP/1.1 absolute-form request targets.** Previously, a request like `GET http://host/a%2Fb` was decoded to a path of `/a/b`, while `GET /a%2Fb` (origin-form) preserved the encoded slash. Both forms now resolve to `/a%2Fb`. Apps that depended on the inconsistent absolute-form behavior should update their routing or middleware to expect the encoded segment ([dotnet/aspnetcore #65930](https://github.com/dotnet/aspnetcore/pull/65930)). +- **`HttpsConnectionAdapterOptions.TlsClientHelloBytesCallback` is obsolete.** Use the new connection middleware shape introduced in [dotnet/aspnetcore #65808](https://github.com/dotnet/aspnetcore/pull/65808). The property continues to work in 11.0 but produces an obsoletion warning. + + + +## Bug fixes + +- **Blazor** + - Fixed an `InvalidOperationException` when navigating to `/Error` in newly scaffolded Blazor Server apps. The template now declares `RequestId` as `public` so `[PersistentState]` can read it ([dotnet/aspnetcore #66245](https://github.com/dotnet/aspnetcore/pull/66245)). + - Fixed a truncated gzip payload for `resource-collection.js.gz` that caused some clients to reject the response. The endpoint now writes the gzip footer before serving the bytes ([dotnet/aspnetcore #66242](https://github.com/dotnet/aspnetcore/pull/66242)). + - Fixed an "Interop methods are already registered for renderer 1" error when a Blazor Server reconnect fell back to resume after the original circuit had expired ([dotnet/aspnetcore #66275](https://github.com/dotnet/aspnetcore/pull/66275)). +- **Routing** + - Fixed `NegotiationMatcherPolicy` invalidating higher-priority endpoints when a lower-priority endpoint shared a DFA path node and carried `ContentEncodingMetadata`. Content-encoding negotiation now disambiguates only between representations of the same resource ([dotnet/aspnetcore #65973](https://github.com/dotnet/aspnetcore/pull/65973)). +- **MVC** + - Fixed `JsonSerializerSettings.DateFormatString` overwriting `DateFormatHandling` when copying serializer settings ([dotnet/aspnetcore #61251](https://github.com/dotnet/aspnetcore/pull/61251)). +- **Templates** + - Renamed the standalone `.NET Web Worker` template to `Blazor Web Worker`, added `InvokeVoidAsync` on `WebWorkerClient`, and added cancellation/timeout support to worker creation and invocation ([dotnet/aspnetcore #66070](https://github.com/dotnet/aspnetcore/pull/66070)). + - Removed the obsolete `net11.0` framework-choice descriptions from the `BlazorWebWorker` template ([dotnet/aspnetcore #66261](https://github.com/dotnet/aspnetcore/pull/66261)). + - Added `Microsoft.Data.SqlClient.Extensions.Azure` to the MVC, Blazor, and Razor Pages Individual Auth templates so Azure App Service deployments using `Authentication=Active Directory Default` work without a separate package install ([dotnet/aspnetcore #66179](https://github.com/dotnet/aspnetcore/pull/66179)). +- **Middleware** + - Clarified `UseStatusCodePages` documentation to call out that the default options handler prefers Problem Details when `IProblemDetailsService` is registered, and falls back to plain text otherwise ([dotnet/aspnetcore #66172](https://github.com/dotnet/aspnetcore/pull/66172)). + +## Community contributors + +Thank you contributors! ❤️ + +- [@Emik03](https://github.com/dotnet/aspnetcore/pulls?q=is%3Apr+is%3Amerged+author%3AEmik03) +- [@kilifu](https://github.com/dotnet/aspnetcore/pulls?q=is%3Apr+is%3Amerged+author%3Akilifu) +- [@marcominerva](https://github.com/dotnet/aspnetcore/pulls?q=is%3Apr+is%3Amerged+author%3Amarcominerva) +- [@MatthewSteeples](https://github.com/dotnet/aspnetcore/pulls?q=is%3Apr+is%3Amerged+author%3AMatthewSteeples) +- [@pedrobsaila](https://github.com/dotnet/aspnetcore/pulls?q=is%3Apr+is%3Amerged+author%3Apedrobsaila) From 33c88bdb4ad182e0b0d4413eb3ff788c14604e9e Mon Sep 17 00:00:00 2001 From: danroth27 Date: Wed, 29 Apr 2026 21:04:21 -0700 Subject: [PATCH 03/15] Add 11.0-preview4 milestone filter to community-contributor links --- release-notes/11.0/preview/preview4/aspnetcore.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/release-notes/11.0/preview/preview4/aspnetcore.md b/release-notes/11.0/preview/preview4/aspnetcore.md index c7301b4f198..4901d836ca1 100644 --- a/release-notes/11.0/preview/preview4/aspnetcore.md +++ b/release-notes/11.0/preview/preview4/aspnetcore.md @@ -178,8 +178,8 @@ Apps that use OpenTelemetry or hot reload aren't affected — the feature switch Thank you contributors! ❤️ -- [@Emik03](https://github.com/dotnet/aspnetcore/pulls?q=is%3Apr+is%3Amerged+author%3AEmik03) -- [@kilifu](https://github.com/dotnet/aspnetcore/pulls?q=is%3Apr+is%3Amerged+author%3Akilifu) -- [@marcominerva](https://github.com/dotnet/aspnetcore/pulls?q=is%3Apr+is%3Amerged+author%3Amarcominerva) -- [@MatthewSteeples](https://github.com/dotnet/aspnetcore/pulls?q=is%3Apr+is%3Amerged+author%3AMatthewSteeples) -- [@pedrobsaila](https://github.com/dotnet/aspnetcore/pulls?q=is%3Apr+is%3Amerged+author%3Apedrobsaila) +- [@Emik03](https://github.com/dotnet/aspnetcore/pulls?q=is%3Apr+is%3Amerged+author%3AEmik03+milestone%3A11.0-preview4) +- [@kilifu](https://github.com/dotnet/aspnetcore/pulls?q=is%3Apr+is%3Amerged+author%3Akilifu+milestone%3A11.0-preview4) +- [@marcominerva](https://github.com/dotnet/aspnetcore/pulls?q=is%3Apr+is%3Amerged+author%3Amarcominerva+milestone%3A11.0-preview4) +- [@MatthewSteeples](https://github.com/dotnet/aspnetcore/pulls?q=is%3Apr+is%3Amerged+author%3AMatthewSteeples+milestone%3A11.0-preview4) +- [@pedrobsaila](https://github.com/dotnet/aspnetcore/pulls?q=is%3Apr+is%3Amerged+author%3Apedrobsaila+milestone%3A11.0-preview4) From 8a10d27591396ae13ac9c40559a9eb64167bbc9c Mon Sep 17 00:00:00 2001 From: danroth27 Date: Thu, 30 Apr 2026 07:00:03 -0700 Subject: [PATCH 04/15] Refine Preview 4 aspnetcore notes after live validation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Live-tested every feature against the installed 11.0.100-preview.4.26224.122 build with reflection + sample apps: * Resolve TLS TODO: confirm ITlsHandshakeFeature.Exception property name. * OpenAPI file results: note .Produces() annotation is required for the binary schema to appear (PR #64562 fires from metadata, not auto-inference). * Endpoint filter on binding failure: drop the inaccurate ValueTask claim (PR #64539 applies to any return type), and call out RouteHandlerOptions.ThrowOnBadRequest = false so the filter observes the 400 in Development mode. Filed dotnet/aspnetcore#66541 for a Virtualize prepend edge case (scroll-anchor regression when `_itemsBefore == 0`); release-notes claim left as-is — the bug only affects the case where the user has not yet scrolled the rendered window past index 0. Circuit-pause and `mcpserver` template remain documented; both PRs were merged late in the cycle and are expected to land in a Preview 4 servicing build. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- release-notes/11.0/preview/preview4/aspnetcore.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/release-notes/11.0/preview/preview4/aspnetcore.md b/release-notes/11.0/preview/preview4/aspnetcore.md index 4901d836ca1..dd6a17ff8b6 100644 --- a/release-notes/11.0/preview/preview4/aspnetcore.md +++ b/release-notes/11.0/preview/preview4/aspnetcore.md @@ -111,12 +111,10 @@ Moving the template into ASP.NET Core makes it discoverable from `dotnet new lis Two related changes make it easier to diagnose and customize TLS connections in Kestrel. -`ITlsHandshakeFeature` now exposes 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 ([dotnet/aspnetcore #65807](https://github.com/dotnet/aspnetcore/pull/65807)). The feature continues to work after the handshake fails — Kestrel snapshots the relevant fields off the underlying `SslStream` before it is disposed. +`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 ([dotnet/aspnetcore #65807](https://github.com/dotnet/aspnetcore/pull/65807)). 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 ([dotnet/aspnetcore #65808](https://github.com/dotnet/aspnetcore/pull/65808)). The previous callback shape is now obsolete; configure ClientHello inspection via the connection-builder pipeline instead. - - ## 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 ([dotnet/aspnetcore #55092](https://github.com/dotnet/aspnetcore/pull/55092)). This prevents shared caches and CDNs from serving a compressed payload to a client that didn't ask for one (or vice versa). @@ -127,8 +125,8 @@ Thank you [@pedrobsaila](https://github.com/pedrobsaila) for this contribution! Two changes round out the minimal API + OpenAPI experience this preview: -- **File 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 ([dotnet/aspnetcore #64562](https://github.com/dotnet/aspnetcore/pull/64562)). Thank you [@marcominerva](https://github.com/marcominerva) for this contribution! -- **Endpoint filters can observe parameter-binding failures.** When a minimal API endpoint has filters configured and its delegate returns `ValueTask`, the filter pipeline now runs even if parameter binding fails. Filters can read the 400 response and substitute their own. Endpoints without filters continue to short-circuit with a 400 as before ([dotnet/aspnetcore #64539](https://github.com/dotnet/aspnetcore/pull/64539)). Thank you [@marcominerva](https://github.com/marcominerva) for this contribution! +- **File 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 ([dotnet/aspnetcore #64562](https://github.com/dotnet/aspnetcore/pull/64562)). Annotate the endpoint with `.Produces(contentType: "application/pdf")` (or the equivalent `*StreamHttpResult`/`FileStreamResult` type) so OpenAPI sees the result type and emits the binary schema. Thank you [@marcominerva](https://github.com/marcominerva) for this contribution! +- **Endpoint filters can 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. Endpoints without filters continue to short-circuit with a 400 as before ([dotnet/aspnetcore #64539](https://github.com/dotnet/aspnetcore/pull/64539)). In Development, set `RouteHandlerOptions.ThrowOnBadRequest = false` so the framework returns a 400 the filter can observe instead of throwing `BadHttpRequestException` to the developer exception page. Thank you [@marcominerva](https://github.com/marcominerva) for this contribution! ## Smaller Blazor WebAssembly publish output From f23ff19d39720b15ea0a90dbf36edd4edb84ecec Mon Sep 17 00:00:00 2001 From: Daniel Roth Date: Thu, 30 Apr 2026 17:54:55 -0700 Subject: [PATCH 05/15] Address PR feedback: HTTP QUERY scope, Virtualize split, runtime-async * HTTP QUERY (#65714) is OpenAPI-only; reword and rename heading. * Virtualize: clarify #65951 covers height-changes-above-viewport (no API change). Add separate section for #66521's new AnchorMode parameter, including ItemComparer guidance for reference-type items. * Add a Runtime-async section for #66449 (backport of #66200). Removes the pre-filtering note that excluded it. Feedback from BrennanConroy and ilonatommy on dotnet/core#10389. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../11.0/preview/preview4/aspnetcore.md | 46 ++++++++++++++++--- 1 file changed, 40 insertions(+), 6 deletions(-) diff --git a/release-notes/11.0/preview/preview4/aspnetcore.md b/release-notes/11.0/preview/preview4/aspnetcore.md index dd6a17ff8b6..c35a0a38a8a 100644 --- a/release-notes/11.0/preview/preview4/aspnetcore.md +++ b/release-notes/11.0/preview/preview4/aspnetcore.md @@ -2,16 +2,18 @@ .NET 11 Preview 4 includes new ASP.NET Core features and improvements: -- [Support for the HTTP QUERY method](#support-for-the-http-query-method) +- [HTTP QUERY in generated OpenAPI documents](#http-query-in-generated-openapi-documents) - [SupplyParameterFromTempData for Blazor](#supplyparameterfromtempdata-for-blazor) - [Server-initiated Blazor Server circuit pause](#server-initiated-blazor-server-circuit-pause) - [Virtualize keeps the viewport stable when content above it changes](#virtualize-keeps-the-viewport-stable-when-content-above-it-changes) +- [Virtualize AnchorMode for stable viewports during prepend and append](#virtualize-anchormode-for-stable-viewports-during-prepend-and-append) - [Blazor WebAssembly service defaults template](#blazor-webassembly-service-defaults-template) - [MCP Server template ships with the .NET SDK](#mcp-server-template-ships-with-the-net-sdk) - [TLS handshake observability in Kestrel](#tls-handshake-observability-in-kestrel) - [Response compression always emits `Vary: Accept-Encoding`](#response-compression-always-emits-vary-accept-encoding) - [OpenAPI and minimal API improvements](#openapi-and-minimal-api-improvements) - [Smaller Blazor WebAssembly publish output](#smaller-blazor-webassembly-publish-output) +- [Runtime-async enabled for shared framework libraries](#runtime-async-enabled-for-shared-framework-libraries) - [Breaking changes](#breaking-changes) - [Bug fixes](#bug-fixes) - [Community contributors](#community-contributors) @@ -20,9 +22,9 @@ ASP.NET Core updates in .NET 11: - [What's new in ASP.NET Core in .NET 11](https://learn.microsoft.com/aspnet/core/release-notes/aspnetcore-11) -## Support for the HTTP QUERY method +## HTTP QUERY in generated OpenAPI documents -ASP.NET Core now recognizes [HTTP QUERY](https://datatracker.ietf.org/doc/draft-ietf-httpbis-safe-method-w-body/) as a first-class HTTP method in routing, minimal APIs, and OpenAPI document generation ([dotnet/aspnetcore #65714](https://github.com/dotnet/aspnetcore/pull/65714)). 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. +OpenAPI document generation now recognizes [HTTP QUERY](https://datatracker.ietf.org/doc/draft-ietf-httpbis-safe-method-w-body/) as a first-class operation type ([dotnet/aspnetcore #65714](https://github.com/dotnet/aspnetcore/pull/65714)). 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 accepted arbitrary verb strings via `MapMethods`; this change makes QUERY endpoints show up correctly in the generated OpenAPI document so client generators and API explorers can consume them. ```csharp var app = WebApplication.Create(); @@ -33,6 +35,8 @@ app.MapMethods("/search", ["QUERY"], (SearchRequest request) => app.Run(); ``` +Thank you [@kilifu](https://github.com/kilifu) for this contribution! + The method shows up in generated OpenAPI documents alongside `GET`, `POST`, and friends, so client generators and API explorers pick it up automatically. Thank you [@kilifu](https://github.com/kilifu) for this contribution! @@ -79,9 +83,34 @@ The method returns `true` when the client was successfully asked to begin pausin ## Virtualize keeps the viewport stable when content above it changes -Preview 3 taught Blazor's `Virtualize` to handle items with different heights. Preview 4 builds on that with viewport stability: visible items stay in place when items above the viewport change height, and when items are prepended to the collection ([dotnet/aspnetcore #65951](https://github.com/dotnet/aspnetcore/pull/65951)). +Blazor's `Virtualize` no longer shifts visible content when items above the viewport change height ([dotnet/aspnetcore #65951](https://github.com/dotnet/aspnetcore/pull/65951)). Previously the component disabled the browser's native scroll anchoring (to avoid an infinite render loop) which meant any height change above the viewport — item expansion, data updates, lazy-loaded content — would cause visible items to jump on screen. + +The fix uses a hybrid approach: native CSS scroll anchoring on browsers that support it for non-`
` layouts, with a manual `ResizeObserver`-based scroll-compensation fallback for `
` layouts and Safari (where native anchoring miscalculates positions on `` candidates). Apps using `Virtualize` get this automatically — no API changes required. + +## Virtualize AnchorMode for stable viewports during prepend and append + +A new `AnchorMode` parameter on `Virtualize` controls viewport behavior at list edges when items are added dynamically — for chat UIs, news feeds, log viewers, and similar scenarios ([dotnet/aspnetcore #66521](https://github.com/dotnet/aspnetcore/pull/66521)). The component snapshots an anchor element before each render and restores its position after, so the visible items stay where the user is looking even when items are inserted before the current viewport (prepend) or after it (append). The mechanism works for both fixed- and variable-height items. + +```razor + + @msg.Text + + +@code { + private static readonly IEqualityComparer _messageById = + EqualityComparer.Create((a, b) => a?.Id == b?.Id, m => m.Id.GetHashCode()); +} +``` + +`AnchorMode` accepts: -The component uses native CSS scroll anchoring where the browser supports it and falls back to manual scroll compensation otherwise (notably for `
` layouts and Safari). Apps using `Virtualize` get this automatically — no API changes required. +- `VirtualizeAnchorMode.Beginning` — preserve viewport when items are prepended (auto-follow the bottom for chat-style UIs). +- `VirtualizeAnchorMode.End` — preserve viewport when items are appended. +- `VirtualizeAnchorMode.None` — no anchoring (default). + +For reference-type items, also supply an `ItemComparer` so the component can correlate items across renders. Without one, `Virtualize` falls back to `EqualityComparer.Default`, which uses reference equality for reference types and won't recognize a re-fetched item as the same logical entity. ## Blazor WebAssembly service defaults template @@ -137,6 +166,12 @@ Two trimming changes shrink published Blazor WebAssembly apps that don't use Ope Apps that use OpenTelemetry or hot reload aren't affected — the feature switches default on in those configurations. +## 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+` ([dotnet/aspnetcore #66449](https://github.com/dotnet/aspnetcore/pull/66449), backporting [#66200](https://github.com/dotnet/aspnetcore/pull/66200)). 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. + ## Breaking changes - **`%2F` is preserved in HTTP/1.1 absolute-form request targets.** Previously, a request like `GET http://host/a%2Fb` was decoded to a path of `/a/b`, while `GET /a%2Fb` (origin-form) preserved the encoded slash. Both forms now resolve to `/a%2Fb`. Apps that depended on the inconsistent absolute-form behavior should update their routing or middleware to expect the encoded segment ([dotnet/aspnetcore #65930](https://github.com/dotnet/aspnetcore/pull/65930)). @@ -151,7 +186,6 @@ Apps that use OpenTelemetry or hot reload aren't affected — the feature switch - Razor editor/IDE fixes (#13017, #13023, #13035, #13043, #13044, #13057, #13063, #13065): tooling/IDE work — product-boundary rule excludes these from ASP.NET Core product notes. - "Detach methods when disconnection confirmed" (#66275) and resource-collection gzip footer (#66242): rolled into Bug fixes. - Blazor Web Worker template renames and clean-up (#66070, #66261): rolled into Bug fixes / templates section. - - Runtime-async enablement for SharedFx-only libraries (#66449): cross-stack runtime-async work; covered in the runtime release notes. - Numerous infra / dependency / loc / agentic-workflow / source-build PRs: pre-filtered (~69 entries) before scoring. --> From 30bc614224cbd7c0b5c3d1ebcf52945e80f084d3 Mon Sep 17 00:00:00 2001 From: Daniel Roth Date: Thu, 30 Apr 2026 23:00:01 -0700 Subject: [PATCH 06/15] Add WebWorker import-map fingerprinting fix to Bug fixes Missed in initial pass: dotnet/aspnetcore#65885 fixes the Blazor Web Worker template failing in published WASM apps because workers don't inherit the page's import map. Belongs alongside the existing WebWorker template entries. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- release-notes/11.0/preview/preview4/aspnetcore.md | 1 + 1 file changed, 1 insertion(+) diff --git a/release-notes/11.0/preview/preview4/aspnetcore.md b/release-notes/11.0/preview/preview4/aspnetcore.md index c35a0a38a8a..deaac816fc0 100644 --- a/release-notes/11.0/preview/preview4/aspnetcore.md +++ b/release-notes/11.0/preview/preview4/aspnetcore.md @@ -195,6 +195,7 @@ Libraries that ship as both shared-framework members and standalone NuGet packag - Fixed an `InvalidOperationException` when navigating to `/Error` in newly scaffolded Blazor Server apps. The template now declares `RequestId` as `public` so `[PersistentState]` can read it ([dotnet/aspnetcore #66245](https://github.com/dotnet/aspnetcore/pull/66245)). - Fixed a truncated gzip payload for `resource-collection.js.gz` that caused some clients to reject the response. The endpoint now writes the gzip footer before serving the bytes ([dotnet/aspnetcore #66242](https://github.com/dotnet/aspnetcore/pull/66242)). - Fixed an "Interop methods are already registered for renderer 1" error when a Blazor Server reconnect fell back to resume after the original circuit had expired ([dotnet/aspnetcore #66275](https://github.com/dotnet/aspnetcore/pull/66275)). + - Fixed the Blazor Web Worker template failing in published Blazor WebAssembly apps. Workers don't inherit the page's import map, so fingerprinted `_framework/dotnet.js` couldn't be resolved from the worker context; the worker client now resolves the URL on the main thread via `import.meta.resolve()` and passes it to the worker through `postMessage` ([dotnet/aspnetcore #65885](https://github.com/dotnet/aspnetcore/pull/65885)). - **Routing** - Fixed `NegotiationMatcherPolicy` invalidating higher-priority endpoints when a lower-priority endpoint shared a DFA path node and carried `ContentEncodingMetadata`. Content-encoding negotiation now disambiguates only between representations of the same resource ([dotnet/aspnetcore #65973](https://github.com/dotnet/aspnetcore/pull/65973)). - **MVC** From eb2d154e3abfbb6afbdb5fc1af98be25233cd509 Mon Sep 17 00:00:00 2001 From: Daniel Roth Date: Fri, 1 May 2026 15:58:02 -0700 Subject: [PATCH 07/15] Address runtime-async PR feedback * Drop backport PR # (use original #66200 only) * Add a call to action for developers to test their apps and report regressions caused by runtime-async enablement. Feedback from BrennanConroy on dotnet/core#10389. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- release-notes/11.0/preview/preview4/aspnetcore.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/release-notes/11.0/preview/preview4/aspnetcore.md b/release-notes/11.0/preview/preview4/aspnetcore.md index deaac816fc0..f1d06043080 100644 --- a/release-notes/11.0/preview/preview4/aspnetcore.md +++ b/release-notes/11.0/preview/preview4/aspnetcore.md @@ -168,10 +168,12 @@ Apps that use OpenTelemetry or hot reload aren't affected — the feature switch ## 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+` ([dotnet/aspnetcore #66449](https://github.com/dotnet/aspnetcore/pull/66449), backporting [#66200](https://github.com/dotnet/aspnetcore/pull/66200)). 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. +ASP.NET Core's shared-framework-only libraries are now compiled with the `runtime-async` feature on `net11.0+` ([dotnet/aspnetcore #66200](https://github.com/dotnet/aspnetcore/pull/66200)). 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, please 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. + ## Breaking changes - **`%2F` is preserved in HTTP/1.1 absolute-form request targets.** Previously, a request like `GET http://host/a%2Fb` was decoded to a path of `/a/b`, while `GET /a%2Fb` (origin-form) preserved the encoded slash. Both forms now resolve to `/a%2Fb`. Apps that depended on the inconsistent absolute-form behavior should update their routing or middleware to expect the encoded segment ([dotnet/aspnetcore #65930](https://github.com/dotnet/aspnetcore/pull/65930)). From f8683e850453a9df4e92b361ed56593dc95cf796 Mon Sep 17 00:00:00 2001 From: Daniel Roth Date: Fri, 1 May 2026 17:31:07 -0700 Subject: [PATCH 08/15] aspnetcore preview4: replace fictional CircuitRegistry example with real CircuitHandler pattern Verified against 11.0.0-preview.4.26230.115: there is no public registry of active circuits. The supported discovery path is CircuitHandler.OnConnectionUpAsync. Updated the example to track circuits in a CircuitHandler and drain them via the captured references. Also clarify return-value semantics and that onPauseRequested is a JS-side option on Blazor.start(). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../11.0/preview/preview4/aspnetcore.md | 36 ++++++++++++++----- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/release-notes/11.0/preview/preview4/aspnetcore.md b/release-notes/11.0/preview/preview4/aspnetcore.md index f1d06043080..ea25252adc7 100644 --- a/release-notes/11.0/preview/preview4/aspnetcore.md +++ b/release-notes/11.0/preview/preview4/aspnetcore.md @@ -64,22 +64,40 @@ The Blazor Identity project template was rewritten to use this attribute instead `Circuit.RequestCircuitPauseAsync` lets server-side code ask the connected Blazor client to begin the graceful circuit-pause flow ([dotnet/aspnetcore #66455](https://github.com/dotnet/aspnetcore/pull/66455)). Until now, pause was only triggered by client-side navigation; this gives operators a programmatic way to drain circuits during deployments or load-balancer rebalancing. +There is no public registry of active circuits, so the supported way to obtain a `Circuit` instance is to capture it from `CircuitHandler.OnConnectionUpAsync`. A small handler that tracks circuits and lets a hosted service drain them looks like this: + ```csharp -public class RebalanceService(CircuitRegistry circuits) +public class CircuitTracker : CircuitHandler { - public async Task DrainAsync(CancellationToken ct) + private static readonly ConcurrentDictionary _circuits = new(); + + public static IReadOnlyCollection ActiveCircuits => _circuits.Values.ToArray(); + + public override Task OnConnectionUpAsync(Circuit circuit, CancellationToken cancellationToken) + { + _circuits[circuit.Id] = circuit; + return Task.CompletedTask; + } + + public override Task OnCircuitClosedAsync(Circuit circuit, CancellationToken cancellationToken) { - foreach (var circuit in circuits.GetActiveCircuits()) - { - await circuit.RequestCircuitPauseAsync(); - } + _circuits.TryRemove(circuit.Id, out _); + return Task.CompletedTask; } } -``` -The method returns `true` when the client was successfully asked to begin pausing. Clients can defer the request via the optional `onPauseRequested` callback in `CircuitStartOptions`. +// In Program.cs: +builder.Services.AddScoped(); +builder.Services.AddScoped(sp => sp.GetRequiredService()); + +// In a drain service: +foreach (var circuit in CircuitTracker.ActiveCircuits) +{ + await circuit.RequestCircuitPauseAsync(); +} +``` - +`RequestCircuitPauseAsync` returns `true` when the client was successfully asked to begin pausing, and `false` if the circuit is already disposed, not yet initialized, or not currently connected. On the JS side, clients can defer the request via the optional `onPauseRequested` callback on the Blazor.start() options. ## Virtualize keeps the viewport stable when content above it changes From 8c2acbf792dd8025138a8227de151c58f5387269 Mon Sep 17 00:00:00 2001 From: Daniel Roth Date: Mon, 4 May 2026 17:03:47 -0700 Subject: [PATCH 09/15] Add Blazor Gateway section to Preview 4 aspnetcore release notes Documents how to opt in to the new Blazor Gateway (PR dotnet/aspnetcore#65982) in a standalone Blazor WebAssembly app: swapping the DevServer reference for Microsoft.AspNetCore.Components.Gateway, configuring YARP proxying to backend APIs, and exposing a runtime config endpoint to the client. Links to the GatewayDemo sample in danroth27/AspNetCore11Samples for an end-to-end setup. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../11.0/preview/preview4/aspnetcore.md | 84 +++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/release-notes/11.0/preview/preview4/aspnetcore.md b/release-notes/11.0/preview/preview4/aspnetcore.md index ea25252adc7..863f274e29f 100644 --- a/release-notes/11.0/preview/preview4/aspnetcore.md +++ b/release-notes/11.0/preview/preview4/aspnetcore.md @@ -8,6 +8,7 @@ - [Virtualize keeps the viewport stable when content above it changes](#virtualize-keeps-the-viewport-stable-when-content-above-it-changes) - [Virtualize AnchorMode for stable viewports during prepend and append](#virtualize-anchormode-for-stable-viewports-during-prepend-and-append) - [Blazor WebAssembly service defaults template](#blazor-webassembly-service-defaults-template) +- [Blazor Gateway for hosting standalone Blazor WebAssembly apps](#blazor-gateway-for-hosting-standalone-blazor-webassembly-apps) - [MCP Server template ships with the .NET SDK](#mcp-server-template-ships-with-the-net-sdk) - [TLS handshake observability in Kestrel](#tls-handshake-observability-in-kestrel) - [Response compression always emits `Vary: Accept-Encoding`](#response-compression-always-emits-vary-accept-encoding) @@ -144,6 +145,89 @@ The generated library wires up OpenTelemetry logging/metrics/tracing with the OT builder.AddBlazorClientServiceDefaults(); ``` +## Blazor Gateway for hosting standalone Blazor WebAssembly apps + +Standalone Blazor WebAssembly apps now have a new development-time host: the **Blazor Gateway** ([dotnet/aspnetcore #65982](https://github.com/dotnet/aspnetcore/pull/65982)). The Blazor Gateway replaces `Microsoft.AspNetCore.Components.WebAssembly.DevServer` with a real ASP.NET Core host that adds two capabilities the Dev Server never had: **YARP-based proxying to backend APIs** and a **runtime config endpoint** that flows JSON configuration to the client. Future templates will use the Blazor Gateway by default, but you can opt in today by swapping the package reference in any standalone Blazor WebAssembly project. + +To switch a standalone Blazor WebAssembly app from the Dev Server to the Gateway, replace the `Microsoft.AspNetCore.Components.WebAssembly.DevServer` reference in the `.csproj`: + +```xml + + + +``` + +`dotnet run` and `dotnet watch` then launch the Gateway instead of the Dev Server. The Gateway reads its configuration from `appsettings.Development.json` (or any standard configuration source), so all of the features below are configured the same way you'd configure any ASP.NET Core app. + +### Proxy `/api/*` to backend services with YARP + +The Gateway hosts [YARP](https://learn.microsoft.com/aspnet/core/fundamentals/servers/yarp/yarp-overview) and reads the standard `ReverseProxy` configuration section. That means you can proxy API calls from the WebAssembly client to one or more backend services without setting up a custom dev-time proxy. For example, this configuration proxies any request that starts with `/api/` to a backend at `http://localhost:5180/`, stripping the `/api` prefix on the way through: + +```json +{ + "ReverseProxy": { + "Routes": { + "weather-api": { + "ClusterId": "backend", + "Match": { "Path": "/api/{**catch-all}" }, + "Transforms": [ + { "PathRemovePrefix": "/api" } + ] + } + }, + "Clusters": { + "backend": { + "Destinations": { + "d1": { "Address": "http://localhost:5180/" } + } + } + } + } +} +``` + +In the Blazor client, `HttpClient` calls to relative URLs like `api/weather` are then routed through the Gateway to the backend — no CORS configuration, no extra middleware in the backend, and no dev-only `BaseAddress` switching: + +```csharp +var forecasts = await Http.GetFromJsonAsync("api/weather"); +``` + +### Flow runtime config to the client through a config endpoint + +The Gateway can also expose a configuration endpoint that returns inline JSON to the WebAssembly client, so settings like `ApiBaseUrl` or feature flags don't have to be baked into the published assets. Configure the endpoint with `ClientApps::ConfigEndpointPath` and `ClientApps::ConfigResponse`: + +```json +{ + "ClientApps": { + "app": { + "ConfigEndpointPath": "/_app/config", + "ConfigResponse": "{ \"apiBaseUrl\": \"/api\", \"featureFlags\": { \"newCheckout\": true } }" + } + } +} +``` + +Fetch the response from a Razor component to drive client-side behavior: + +```csharp +@inject HttpClient Http + +@code { + AppConfig? config; + + protected override async Task OnInitializedAsync() + { + config = await Http.GetFromJsonAsync("_app/config"); + } + + record AppConfig(string ApiBaseUrl, Dictionary FeatureFlags); +} +``` + +### End-to-end sample + +A complete working setup — standalone Blazor WebAssembly client, ASP.NET Core backend, and a Gateway wired up with both YARP proxying and a config endpoint — is in the [`GatewayDemo` sample](https://github.com/danroth27/AspNetCore11Samples/tree/dotnet11p4/GatewayDemo). The sample also shows how to wire up [`Microsoft.Extensions.ServiceDiscovery`](https://learn.microsoft.com/dotnet/core/extensions/service-discovery) so YARP destinations can be resolved by service name in addition to absolute URLs. + ## 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 ([dotnet/aspnetcore #66520](https://github.com/dotnet/aspnetcore/pull/66520)). From ae8d68ec3aa0aa499379918597b64293ffd11be4 Mon Sep 17 00:00:00 2001 From: Daniel Roth Date: Mon, 4 May 2026 17:47:19 -0700 Subject: [PATCH 10/15] Update Blazor Gateway sample link in P4 release notes Sample was restructured to live in BlazorWasmFeatures/ alongside sibling BackendApi/ and Gateway/ projects (replacing the GatewayDemo/ subtree). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- release-notes/11.0/preview/preview4/aspnetcore.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release-notes/11.0/preview/preview4/aspnetcore.md b/release-notes/11.0/preview/preview4/aspnetcore.md index 863f274e29f..728d9411c47 100644 --- a/release-notes/11.0/preview/preview4/aspnetcore.md +++ b/release-notes/11.0/preview/preview4/aspnetcore.md @@ -226,7 +226,7 @@ Fetch the response from a Razor component to drive client-side behavior: ### End-to-end sample -A complete working setup — standalone Blazor WebAssembly client, ASP.NET Core backend, and a Gateway wired up with both YARP proxying and a config endpoint — is in the [`GatewayDemo` sample](https://github.com/danroth27/AspNetCore11Samples/tree/dotnet11p4/GatewayDemo). The sample also shows how to wire up [`Microsoft.Extensions.ServiceDiscovery`](https://learn.microsoft.com/dotnet/core/extensions/service-discovery) so YARP destinations can be resolved by service name in addition to absolute URLs. +A complete working setup — standalone Blazor WebAssembly client, ASP.NET Core backend, and a Gateway wired up with both YARP proxying and a config endpoint — is in the [`BlazorWasmFeatures` sample](https://github.com/danroth27/AspNetCore11Samples/tree/dotnet11p4/BlazorWasmFeatures) (with sibling `BackendApi/` and `Gateway/` projects). ## MCP Server template ships with the .NET SDK From 69efa67f602397e83025508b33f4bc74e13cc323 Mon Sep 17 00:00:00 2001 From: Copilot <223556219+Copilot@users.noreply.github.com> Date: Mon, 4 May 2026 21:18:53 -0700 Subject: [PATCH 11/15] Remove Blazor Gateway section from Preview 4 release notes The Microsoft.AspNetCore.Components.Gateway package isn't being produced on the build feeds (the Web SDK defaults IsPackable to false, silently no-op'ing Pack). A fix is up at dotnet/aspnetcore#66579 but won't make Preview 4, so remove the section and TOC entry; we'll bring it back when the package ships. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../11.0/preview/preview4/aspnetcore.md | 84 ------------------- 1 file changed, 84 deletions(-) diff --git a/release-notes/11.0/preview/preview4/aspnetcore.md b/release-notes/11.0/preview/preview4/aspnetcore.md index 728d9411c47..ea25252adc7 100644 --- a/release-notes/11.0/preview/preview4/aspnetcore.md +++ b/release-notes/11.0/preview/preview4/aspnetcore.md @@ -8,7 +8,6 @@ - [Virtualize keeps the viewport stable when content above it changes](#virtualize-keeps-the-viewport-stable-when-content-above-it-changes) - [Virtualize AnchorMode for stable viewports during prepend and append](#virtualize-anchormode-for-stable-viewports-during-prepend-and-append) - [Blazor WebAssembly service defaults template](#blazor-webassembly-service-defaults-template) -- [Blazor Gateway for hosting standalone Blazor WebAssembly apps](#blazor-gateway-for-hosting-standalone-blazor-webassembly-apps) - [MCP Server template ships with the .NET SDK](#mcp-server-template-ships-with-the-net-sdk) - [TLS handshake observability in Kestrel](#tls-handshake-observability-in-kestrel) - [Response compression always emits `Vary: Accept-Encoding`](#response-compression-always-emits-vary-accept-encoding) @@ -145,89 +144,6 @@ The generated library wires up OpenTelemetry logging/metrics/tracing with the OT builder.AddBlazorClientServiceDefaults(); ``` -## Blazor Gateway for hosting standalone Blazor WebAssembly apps - -Standalone Blazor WebAssembly apps now have a new development-time host: the **Blazor Gateway** ([dotnet/aspnetcore #65982](https://github.com/dotnet/aspnetcore/pull/65982)). The Blazor Gateway replaces `Microsoft.AspNetCore.Components.WebAssembly.DevServer` with a real ASP.NET Core host that adds two capabilities the Dev Server never had: **YARP-based proxying to backend APIs** and a **runtime config endpoint** that flows JSON configuration to the client. Future templates will use the Blazor Gateway by default, but you can opt in today by swapping the package reference in any standalone Blazor WebAssembly project. - -To switch a standalone Blazor WebAssembly app from the Dev Server to the Gateway, replace the `Microsoft.AspNetCore.Components.WebAssembly.DevServer` reference in the `.csproj`: - -```xml - - - -``` - -`dotnet run` and `dotnet watch` then launch the Gateway instead of the Dev Server. The Gateway reads its configuration from `appsettings.Development.json` (or any standard configuration source), so all of the features below are configured the same way you'd configure any ASP.NET Core app. - -### Proxy `/api/*` to backend services with YARP - -The Gateway hosts [YARP](https://learn.microsoft.com/aspnet/core/fundamentals/servers/yarp/yarp-overview) and reads the standard `ReverseProxy` configuration section. That means you can proxy API calls from the WebAssembly client to one or more backend services without setting up a custom dev-time proxy. For example, this configuration proxies any request that starts with `/api/` to a backend at `http://localhost:5180/`, stripping the `/api` prefix on the way through: - -```json -{ - "ReverseProxy": { - "Routes": { - "weather-api": { - "ClusterId": "backend", - "Match": { "Path": "/api/{**catch-all}" }, - "Transforms": [ - { "PathRemovePrefix": "/api" } - ] - } - }, - "Clusters": { - "backend": { - "Destinations": { - "d1": { "Address": "http://localhost:5180/" } - } - } - } - } -} -``` - -In the Blazor client, `HttpClient` calls to relative URLs like `api/weather` are then routed through the Gateway to the backend — no CORS configuration, no extra middleware in the backend, and no dev-only `BaseAddress` switching: - -```csharp -var forecasts = await Http.GetFromJsonAsync("api/weather"); -``` - -### Flow runtime config to the client through a config endpoint - -The Gateway can also expose a configuration endpoint that returns inline JSON to the WebAssembly client, so settings like `ApiBaseUrl` or feature flags don't have to be baked into the published assets. Configure the endpoint with `ClientApps::ConfigEndpointPath` and `ClientApps::ConfigResponse`: - -```json -{ - "ClientApps": { - "app": { - "ConfigEndpointPath": "/_app/config", - "ConfigResponse": "{ \"apiBaseUrl\": \"/api\", \"featureFlags\": { \"newCheckout\": true } }" - } - } -} -``` - -Fetch the response from a Razor component to drive client-side behavior: - -```csharp -@inject HttpClient Http - -@code { - AppConfig? config; - - protected override async Task OnInitializedAsync() - { - config = await Http.GetFromJsonAsync("_app/config"); - } - - record AppConfig(string ApiBaseUrl, Dictionary FeatureFlags); -} -``` - -### End-to-end sample - -A complete working setup — standalone Blazor WebAssembly client, ASP.NET Core backend, and a Gateway wired up with both YARP proxying and a config endpoint — is in the [`BlazorWasmFeatures` sample](https://github.com/danroth27/AspNetCore11Samples/tree/dotnet11p4/BlazorWasmFeatures) (with sibling `BackendApi/` and `Gateway/` projects). - ## 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 ([dotnet/aspnetcore #66520](https://github.com/dotnet/aspnetcore/pull/66520)). From 7722c18da6943b1f30cc4da3fc1f72fa9fb1009b Mon Sep 17 00:00:00 2001 From: Copilot <223556219+Copilot@users.noreply.github.com> Date: Tue, 5 May 2026 17:53:26 -0700 Subject: [PATCH 12/15] Address Brennan's review feedback on aspnetcore.md - Remove duplicate 'Thank you @kilifu' line in HTTP QUERY section. - Add UseTlsClientHelloListener snippet to the TLS section. - Split 'OpenAPI and minimal API improvements' into two sections so the endpoint-filter bullet (not OpenAPI-related) stands on its own. - Remove the agent-generated 'Filtered features' HTML comment block. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../11.0/preview/preview4/aspnetcore.md | 48 +++++++++++-------- 1 file changed, 28 insertions(+), 20 deletions(-) diff --git a/release-notes/11.0/preview/preview4/aspnetcore.md b/release-notes/11.0/preview/preview4/aspnetcore.md index ea25252adc7..0f1903271b0 100644 --- a/release-notes/11.0/preview/preview4/aspnetcore.md +++ b/release-notes/11.0/preview/preview4/aspnetcore.md @@ -11,7 +11,8 @@ - [MCP Server template ships with the .NET SDK](#mcp-server-template-ships-with-the-net-sdk) - [TLS handshake observability in Kestrel](#tls-handshake-observability-in-kestrel) - [Response compression always emits `Vary: Accept-Encoding`](#response-compression-always-emits-vary-accept-encoding) -- [OpenAPI and minimal API improvements](#openapi-and-minimal-api-improvements) +- [File result types appear in OpenAPI documents](#file-result-types-appear-in-openapi-documents) +- [Endpoint filters observe parameter-binding failures](#endpoint-filters-observe-parameter-binding-failures) - [Smaller Blazor WebAssembly publish output](#smaller-blazor-webassembly-publish-output) - [Runtime-async enabled for shared framework libraries](#runtime-async-enabled-for-shared-framework-libraries) - [Breaking changes](#breaking-changes) @@ -35,8 +36,6 @@ app.MapMethods("/search", ["QUERY"], (SearchRequest request) => app.Run(); ``` -Thank you [@kilifu](https://github.com/kilifu) for this contribution! - The method shows up in generated OpenAPI documents alongside `GET`, `POST`, and friends, so client generators and API explorers pick it up automatically. Thank you [@kilifu](https://github.com/kilifu) for this contribution! @@ -160,7 +159,23 @@ Two related changes make it easier to diagnose and customize TLS connections in `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 ([dotnet/aspnetcore #65807](https://github.com/dotnet/aspnetcore/pull/65807)). 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 ([dotnet/aspnetcore #65808](https://github.com/dotnet/aspnetcore/pull/65808)). The previous callback shape is now obsolete; configure ClientHello inspection via the connection-builder pipeline instead. +The `TlsClientHelloBytesCallback` option on `HttpsConnectionAdapterOptions` was reworked as a connection middleware ([dotnet/aspnetcore #65808](https://github.com/dotnet/aspnetcore/pull/65808)). The previous callback shape is now obsolete; configure ClientHello inspection via the new `ListenOptions.UseTlsClientHelloListener` extension instead: + +```csharp +var builder = WebApplication.CreateBuilder(args); + +builder.WebHost.ConfigureKestrel(options => +{ + options.ListenAnyIP(5001, listenOptions => + { + listenOptions.UseHttps(); + listenOptions.UseTlsClientHelloListener((connection, clientHelloBytes) => + { + // Inspect the ClientHello (SNI, ALPN, etc.) for diagnostics or routing. + }); + }); +}); +``` ## Response compression always emits `Vary: Accept-Encoding` @@ -168,12 +183,17 @@ The response-compression middleware now adds `Vary: Accept-Encoding` to every re Thank you [@pedrobsaila](https://github.com/pedrobsaila) for this contribution! -## OpenAPI and minimal API improvements +## File 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 ([dotnet/aspnetcore #64562](https://github.com/dotnet/aspnetcore/pull/64562)). Annotate the endpoint with `.Produces(contentType: "application/pdf")` (or the equivalent `*StreamHttpResult`/`FileStreamResult` type) so OpenAPI sees the result type and emits the binary schema. -Two changes round out the minimal API + OpenAPI experience this preview: +Thank you [@marcominerva](https://github.com/marcominerva) for this contribution! -- **File 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 ([dotnet/aspnetcore #64562](https://github.com/dotnet/aspnetcore/pull/64562)). Annotate the endpoint with `.Produces(contentType: "application/pdf")` (or the equivalent `*StreamHttpResult`/`FileStreamResult` type) so OpenAPI sees the result type and emits the binary schema. Thank you [@marcominerva](https://github.com/marcominerva) for this contribution! -- **Endpoint filters can 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. Endpoints without filters continue to short-circuit with a 400 as before ([dotnet/aspnetcore #64539](https://github.com/dotnet/aspnetcore/pull/64539)). In Development, set `RouteHandlerOptions.ThrowOnBadRequest = false` so the framework returns a 400 the filter can observe instead of throwing `BadHttpRequestException` to the developer exception page. Thank you [@marcominerva](https://github.com/marcominerva) for this contribution! +## 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 ([dotnet/aspnetcore #64539](https://github.com/dotnet/aspnetcore/pull/64539)). Filters can read `HttpContext.Response.StatusCode == 400` and substitute their own response body. Endpoints without filters continue to short-circuit with a 400 as before. In Development, set `RouteHandlerOptions.ThrowOnBadRequest = false` so the framework returns a 400 the filter can observe instead of throwing `BadHttpRequestException` to the developer exception page. + +Thank you [@marcominerva](https://github.com/marcominerva) for this contribution! ## Smaller Blazor WebAssembly publish output @@ -197,18 +217,6 @@ Because runtime-async changes how `async`/`await` is generated for a large porti - **`%2F` is preserved in HTTP/1.1 absolute-form request targets.** Previously, a request like `GET http://host/a%2Fb` was decoded to a path of `/a/b`, while `GET /a%2Fb` (origin-form) preserved the encoded slash. Both forms now resolve to `/a%2Fb`. Apps that depended on the inconsistent absolute-form behavior should update their routing or middleware to expect the encoded segment ([dotnet/aspnetcore #65930](https://github.com/dotnet/aspnetcore/pull/65930)). - **`HttpsConnectionAdapterOptions.TlsClientHelloBytesCallback` is obsolete.** Use the new connection middleware shape introduced in [dotnet/aspnetcore #65808](https://github.com/dotnet/aspnetcore/pull/65808). The property continues to work in 11.0 but produces an obsoletion warning. - - ## Bug fixes - **Blazor** From 83d32ce005f5f04d9548cd4f6898457f22d24534 Mon Sep 17 00:00:00 2001 From: Copilot <223556219+Copilot@users.noreply.github.com> Date: Wed, 6 May 2026 16:18:16 -0700 Subject: [PATCH 13/15] Address halter73, mikekistler, DeagleGross feedback - HTTP QUERY: replace 'first-class' with 'known' (per @halter73), and expand with OpenAPI 3.2 'query' Path Item field details and the 'OpenApiVersion'/'x-oai-additionalOperations' note (per @mikekistler). - HTTP QUERY follow-up sentence: lowercase 'get'/'post' to match OpenAPI spec casing (per @mikekistler). - TLS section: replace standalone snippet with combined snippet that also demonstrates 'ITlsHandshakeFeature.Exception' via connection middleware, and ensures 'UseTlsClientHelloListener' is called before 'UseHttps' (per @DeagleGross + @halter73 + @BrennanConroy). - Endpoint filters: drop the redundant short-circuit sentence, clarify 'ThrowOnBadRequest' default in non-Development environments, and call out the 10.0.8 backport (per @halter73). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../11.0/preview/preview4/aspnetcore.md | 30 +++++++++++++++---- 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/release-notes/11.0/preview/preview4/aspnetcore.md b/release-notes/11.0/preview/preview4/aspnetcore.md index 0f1903271b0..becfe377c6f 100644 --- a/release-notes/11.0/preview/preview4/aspnetcore.md +++ b/release-notes/11.0/preview/preview4/aspnetcore.md @@ -25,7 +25,9 @@ ASP.NET Core updates in .NET 11: ## 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 first-class operation type ([dotnet/aspnetcore #65714](https://github.com/dotnet/aspnetcore/pull/65714)). 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 accepted arbitrary verb strings via `MapMethods`; this change makes QUERY endpoints show up correctly in the generated OpenAPI document so client generators and API explorers can consume them. +OpenAPI document generation now recognizes [HTTP QUERY](https://datatracker.ietf.org/doc/draft-ietf-httpbis-safe-method-w-body/) as a known operation type ([dotnet/aspnetcore #65714](https://github.com/dotnet/aspnetcore/pull/65714)). 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 accepted 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. This change makes QUERY endpoints show up correctly in the generated OpenAPI document so client generators and API explorers can consume them. + +Note that `query` is only valid in an OpenAPI 3.2 document, so you'll want to set the `OpenApiVersion` in the `OpenApiOptions`. In earlier OpenAPI versions, the `query` operation is generated within a `x-oai-additionalOperations` specification extension in the Path Item Object. ```csharp var app = WebApplication.Create(); @@ -36,7 +38,7 @@ app.MapMethods("/search", ["QUERY"], (SearchRequest request) => app.Run(); ``` -The method shows up in generated OpenAPI documents alongside `GET`, `POST`, and friends, so client generators and API explorers pick it up automatically. +The method shows up in generated OpenAPI documents alongside `get`, `post`, and friends, so client generators and API explorers pick it up automatically. Thank you [@kilifu](https://github.com/kilifu) for this contribution! @@ -159,7 +161,7 @@ Two related changes make it easier to diagnose and customize TLS connections in `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 ([dotnet/aspnetcore #65807](https://github.com/dotnet/aspnetcore/pull/65807)). 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 ([dotnet/aspnetcore #65808](https://github.com/dotnet/aspnetcore/pull/65808)). The previous callback shape is now obsolete; configure ClientHello inspection via the new `ListenOptions.UseTlsClientHelloListener` extension instead: +The `TlsClientHelloBytesCallback` option on `HttpsConnectionAdapterOptions` was reworked as a connection middleware ([dotnet/aspnetcore #65808](https://github.com/dotnet/aspnetcore/pull/65808)). 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); @@ -168,11 +170,23 @@ builder.WebHost.ConfigureKestrel(options => { options.ListenAnyIP(5001, listenOptions => { - listenOptions.UseHttps(); + 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) => { - // Inspect the ClientHello (SNI, ALPN, etc.) for diagnostics or routing. + Console.WriteLine($"TLS Client Hello received on {connection.ConnectionId}, {clientHelloBytes.Length} bytes"); }); + listenOptions.UseHttps(); }); }); ``` @@ -191,7 +205,11 @@ Thank you [@marcominerva](https://github.com/marcominerva) for this contribution ## 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 ([dotnet/aspnetcore #64539](https://github.com/dotnet/aspnetcore/pull/64539)). Filters can read `HttpContext.Response.StatusCode == 400` and substitute their own response body. Endpoints without filters continue to short-circuit with a 400 as before. In Development, set `RouteHandlerOptions.ThrowOnBadRequest = false` so the framework returns a 400 the filter can observe instead of throwing `BadHttpRequestException` to the developer exception page. +When a minimal API endpoint has any filters or filter factories configured, the filter pipeline now runs even if parameter binding fails ([dotnet/aspnetcore #64539](https://github.com/dotnet/aspnetcore/pull/64539)). 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 the filter can observe instead of throwing `BadHttpRequestException` to the developer exception page. This is already the default in non-`Development` environments. + +This change has also been backported to 10.0.8. Thank you [@marcominerva](https://github.com/marcominerva) for this contribution! From cb4a9fe1ac5e1ab63ac1b1cbbf22580065443ceb Mon Sep 17 00:00:00 2001 From: Daniel Roth Date: Fri, 8 May 2026 13:50:31 -0700 Subject: [PATCH 14/15] Address final review pass - Expand HTTP QUERY snippet with OpenApiVersion=3.2 setup and show generated output for both 3.2 (inline `query`) and 3.0/3.1 (`x-oai-additionalOperations`). - Fix article agreement: `a x-oai-additionalOperations` -> `an x-oai-additionalOperations`. - Correct the Virtualize AnchorMode descriptions to match the XML doc comments on the enum (Beginning = news feed/notification UX, End = chat/log auto-scroll UX, default is Beginning, `[Flags]` so they can be combined). Update the example to use `notifications` to match the corrected scenario. - Promote the Blazor Web Worker template rename + `InvokeVoidAsync` + cancellation/ timeout additions to its own section instead of a Bug fixes bullet. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../11.0/preview/preview4/aspnetcore.md | 80 ++++++++++++++++--- 1 file changed, 67 insertions(+), 13 deletions(-) diff --git a/release-notes/11.0/preview/preview4/aspnetcore.md b/release-notes/11.0/preview/preview4/aspnetcore.md index becfe377c6f..1ba1ec471c8 100644 --- a/release-notes/11.0/preview/preview4/aspnetcore.md +++ b/release-notes/11.0/preview/preview4/aspnetcore.md @@ -9,6 +9,7 @@ - [Virtualize AnchorMode for stable viewports during prepend and append](#virtualize-anchormode-for-stable-viewports-during-prepend-and-append) - [Blazor WebAssembly service defaults template](#blazor-webassembly-service-defaults-template) - [MCP Server template ships with the .NET SDK](#mcp-server-template-ships-with-the-net-sdk) +- [Blazor Web Worker template updates](#blazor-web-worker-template-updates) - [TLS handshake observability in Kestrel](#tls-handshake-observability-in-kestrel) - [Response compression always emits `Vary: Accept-Encoding`](#response-compression-always-emits-vary-accept-encoding) - [File result types appear in OpenAPI documents](#file-result-types-appear-in-openapi-documents) @@ -27,10 +28,21 @@ ASP.NET Core updates in .NET 11: OpenAPI document generation now recognizes [HTTP QUERY](https://datatracker.ietf.org/doc/draft-ietf-httpbis-safe-method-w-body/) as a known operation type ([dotnet/aspnetcore #65714](https://github.com/dotnet/aspnetcore/pull/65714)). 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 accepted 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. This change makes QUERY endpoints show up correctly in the generated OpenAPI document so client generators and API explorers can consume them. -Note that `query` is only valid in an OpenAPI 3.2 document, so you'll want to set the `OpenApiVersion` in the `OpenApiOptions`. In earlier OpenAPI versions, the `query` operation is generated within a `x-oai-additionalOperations` specification extension in the Path Item Object. +Note that `query` is only valid in an OpenAPI 3.2 document, so you'll want to 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 -var app = WebApplication.Create(); +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)); @@ -38,6 +50,34 @@ app.MapMethods("/search", ["QUERY"], (SearchRequest request) => app.Run(); ``` +In an OpenAPI 3.2 document, the QUERY operation is described inline as a sibling of `get`, `post`, and friends: + +```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": { ... } } + } + } + } +} +``` + The method shows up in generated OpenAPI documents alongside `get`, `post`, and friends, so client generators and API explorers pick it up automatically. Thank you [@kilifu](https://github.com/kilifu) for this contribution! @@ -111,23 +151,25 @@ The fix uses a hybrid approach: native CSS scroll anchoring on browsers that sup A new `AnchorMode` parameter on `Virtualize` controls viewport behavior at list edges when items are added dynamically — for chat UIs, news feeds, log viewers, and similar scenarios ([dotnet/aspnetcore #66521](https://github.com/dotnet/aspnetcore/pull/66521)). The component snapshots an anchor element before each render and restores its position after, so the visible items stay where the user is looking even when items are inserted before the current viewport (prepend) or after it (append). The mechanism works for both fixed- and variable-height items. ```razor - - @msg.Text + ItemComparer="@_notificationById"> + @item.Text @code { - private static readonly IEqualityComparer _messageById = - EqualityComparer.Create((a, b) => a?.Id == b?.Id, m => m.Id.GetHashCode()); + private static readonly IEqualityComparer _notificationById = + EqualityComparer.Create((a, b) => a?.Id == b?.Id, n => n.Id.GetHashCode()); } ``` -`AnchorMode` accepts: +`AnchorMode` is a `[Flags]` enum that accepts: + +- `VirtualizeAnchorMode.Beginning` (default) — pins the first visible item. When the user is at or near the top of the list and items are prepended, the viewport stays at the top showing the newest items — matching news feed and notification list UX. +- `VirtualizeAnchorMode.End` — pins the last visible item. When the user is at or near the bottom of the list and items are appended, the viewport auto-scrolls to show them — matching chat and log viewer UX. If the user has scrolled away, auto-scroll disengages until they return to the bottom. +- `VirtualizeAnchorMode.None` — no edge pinning. The viewport stays at its current scroll position regardless of item changes. -- `VirtualizeAnchorMode.Beginning` — preserve viewport when items are prepended (auto-follow the bottom for chat-style UIs). -- `VirtualizeAnchorMode.End` — preserve viewport when items are appended. -- `VirtualizeAnchorMode.None` — no anchoring (default). +Combine `Beginning | End` to pin both edges. For reference-type items, also supply an `ItemComparer` so the component can correlate items across renders. Without one, `Virtualize` falls back to `EqualityComparer.Default`, which uses reference equality for reference types and won't recognize a re-fetched item as the same logical entity. @@ -155,6 +197,19 @@ 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. +## Blazor Web Worker template updates + +The standalone `.NET Web Worker` template — which scaffolds a Web Worker client for offloading long-running work to a background thread in Blazor WebAssembly and Blazor Web apps — was renamed to **`Blazor Web Worker`** to make it clearer that it's part of the Blazor stack ([dotnet/aspnetcore #66070](https://github.com/dotnet/aspnetcore/pull/66070)). The same change adds two often-requested capabilities to the generated `WebWorkerClient`: + +- **`InvokeVoidAsync`** for fire-and-forget worker calls that don't return a value, mirroring the shape on `IJSRuntime`. +- **Cancellation and timeout support** on both worker creation and worker invocations, so callers can pass a `CancellationToken` and tear down a stuck worker cleanly. + +```bash +dotnet new blazorwebworker -o MyApp.Worker +``` + +Existing projects created with the old template continue to work — the rename only affects the template name shown in `dotnet new list` and Visual Studio. + ## TLS handshake observability in Kestrel Two related changes make it easier to diagnose and customize TLS connections in Kestrel. @@ -247,9 +302,8 @@ Because runtime-async changes how `async`/`await` is generated for a large porti - **MVC** - Fixed `JsonSerializerSettings.DateFormatString` overwriting `DateFormatHandling` when copying serializer settings ([dotnet/aspnetcore #61251](https://github.com/dotnet/aspnetcore/pull/61251)). - **Templates** - - Renamed the standalone `.NET Web Worker` template to `Blazor Web Worker`, added `InvokeVoidAsync` on `WebWorkerClient`, and added cancellation/timeout support to worker creation and invocation ([dotnet/aspnetcore #66070](https://github.com/dotnet/aspnetcore/pull/66070)). - - Removed the obsolete `net11.0` framework-choice descriptions from the `BlazorWebWorker` template ([dotnet/aspnetcore #66261](https://github.com/dotnet/aspnetcore/pull/66261)). - Added `Microsoft.Data.SqlClient.Extensions.Azure` to the MVC, Blazor, and Razor Pages Individual Auth templates so Azure App Service deployments using `Authentication=Active Directory Default` work without a separate package install ([dotnet/aspnetcore #66179](https://github.com/dotnet/aspnetcore/pull/66179)). + - Removed the obsolete `net11.0` framework-choice descriptions from the `BlazorWebWorker` template ([dotnet/aspnetcore #66261](https://github.com/dotnet/aspnetcore/pull/66261)). - **Middleware** - Clarified `UseStatusCodePages` documentation to call out that the default options handler prefers Problem Details when `IProblemDetailsService` is registered, and falls back to plain text otherwise ([dotnet/aspnetcore #66172](https://github.com/dotnet/aspnetcore/pull/66172)). From 237ea22171f088fa1b8ab75ff00aa8312c7775b4 Mon Sep 17 00:00:00 2001 From: Daniel Roth Date: Fri, 8 May 2026 14:01:05 -0700 Subject: [PATCH 15/15] Drop redundant 'method shows up in OpenAPI' summary line The two JSON snippets above already show exactly where QUERY appears in the generated document for both 3.2 and 3.0/3.1, so the trailing prose is now redundant. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- release-notes/11.0/preview/preview4/aspnetcore.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/release-notes/11.0/preview/preview4/aspnetcore.md b/release-notes/11.0/preview/preview4/aspnetcore.md index 1ba1ec471c8..502d8736b41 100644 --- a/release-notes/11.0/preview/preview4/aspnetcore.md +++ b/release-notes/11.0/preview/preview4/aspnetcore.md @@ -78,8 +78,6 @@ In OpenAPI 3.0 and 3.1 documents, the same operation is represented under the `x } ``` -The method shows up in generated OpenAPI documents alongside `get`, `post`, and friends, so client generators and API explorers pick it up automatically. - Thank you [@kilifu](https://github.com/kilifu) for this contribution! ## `SupplyParameterFromTempData` for Blazor