From e587498135f93880ddbea094f123ae1eaac962be Mon Sep 17 00:00:00 2001 From: Stephen Halter Date: Thu, 4 Jun 2026 14:23:34 -0700 Subject: [PATCH 1/5] Port .NET 11 ASP.NET Core breaking changes from dotnet/docs#54188 Adds 12 per-entry pages plus an overview under aspnetcore/breaking-changes/11/ and wires them into aspnetcore/toc.yml under the existing ASP.NET Core 11 group, mirroring the .NET 10 layout established in #36693. Relative category links (../../categories.md#...) rewritten to the absolute /dotnet/core/compatibility/categories#... form used elsewhere in this repo. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../11/blazor-custom-event-name-collision.md | 68 ++++++++++++ ...blazor-enhanced-nav-preloading-disabled.md | 50 +++++++++ .../11/blazor-obsolete-apis-removed.md | 89 +++++++++++++++ .../11/concurrencylimiter-removed.md | 101 ++++++++++++++++++ .../11/http-activity-otel-semconv.md | 63 +++++++++++ .../11/kestrel-strict-protocol-compliance.md | 60 +++++++++++ .../11/microsoft-openapi-3x.md | 71 ++++++++++++ .../11/openapi-server-url-trailing-slash.md | 76 +++++++++++++ aspnetcore/breaking-changes/11/overview.md | 28 +++++ ...ey-signin-enforces-confirmation-lockout.md | 53 +++++++++ .../11/response-compression-always-vary.md | 61 +++++++++++ .../11/sqlclient-azure-extensions-required.md | 53 +++++++++ .../11/wasm-env-vars-in-configuration.md | 67 ++++++++++++ aspnetcore/toc.yml | 28 +++++ 14 files changed, 868 insertions(+) create mode 100644 aspnetcore/breaking-changes/11/blazor-custom-event-name-collision.md create mode 100644 aspnetcore/breaking-changes/11/blazor-enhanced-nav-preloading-disabled.md create mode 100644 aspnetcore/breaking-changes/11/blazor-obsolete-apis-removed.md create mode 100644 aspnetcore/breaking-changes/11/concurrencylimiter-removed.md create mode 100644 aspnetcore/breaking-changes/11/http-activity-otel-semconv.md create mode 100644 aspnetcore/breaking-changes/11/kestrel-strict-protocol-compliance.md create mode 100644 aspnetcore/breaking-changes/11/microsoft-openapi-3x.md create mode 100644 aspnetcore/breaking-changes/11/openapi-server-url-trailing-slash.md create mode 100644 aspnetcore/breaking-changes/11/overview.md create mode 100644 aspnetcore/breaking-changes/11/passkey-signin-enforces-confirmation-lockout.md create mode 100644 aspnetcore/breaking-changes/11/response-compression-always-vary.md create mode 100644 aspnetcore/breaking-changes/11/sqlclient-azure-extensions-required.md create mode 100644 aspnetcore/breaking-changes/11/wasm-env-vars-in-configuration.md diff --git a/aspnetcore/breaking-changes/11/blazor-custom-event-name-collision.md b/aspnetcore/breaking-changes/11/blazor-custom-event-name-collision.md new file mode 100644 index 000000000000..a6d6bf4b2ed9 --- /dev/null +++ b/aspnetcore/breaking-changes/11/blazor-custom-event-name-collision.md @@ -0,0 +1,68 @@ +--- +title: "Breaking change: Blazor custom event registration throws when name matches a browser event" +description: "Learn about the breaking change in ASP.NET Core 11 where Blazor.registerCustomEventType throws when eventName equals browserEventName." +ms.date: 06/04/2026 +ai-usage: ai-assisted +--- + +# Blazor custom event registration throws when name matches a browser event + +The Blazor JavaScript API `Blazor.registerCustomEventType` now throws an error when the custom event name matches its `browserEventName` option. Registering a custom event with the same name as the underlying browser event caused the event to fire twice for each user action. + +## Version introduced + +.NET 11 + +## Previous behavior + +Previously, you could call `Blazor.registerCustomEventType` with an `eventName` equal to the `browserEventName` option. The call succeeded silently, but every browser event of that name caused two `EventCallback` invocations on the .NET side: one for the native browser event and one for the custom event wrapper that re-fired the same name. + +```javascript +// This used to silently double-fire the event. +Blazor.registerCustomEventType('scrolltop', { + browserEventName: 'scrolltop' +}); +``` + +## New behavior + +Starting in ASP.NET Core 11, `Blazor.registerCustomEventType` throws when `eventName` equals `options.browserEventName`: + +```javascript +// This now throws synchronously. +Blazor.registerCustomEventType('scrolltop', { + browserEventName: 'scrolltop' +}); +``` + +To wrap a browser event in a custom event without the collision, give the custom event a different name: + +```javascript +Blazor.registerCustomEventType('customscroll', { + browserEventName: 'scrolltop' +}); +``` + +## Type of breaking change + +This change is a [behavioral change](/dotnet/core/compatibility/categories#behavioral-change). + +## Reason for change + +Calling `Blazor.registerCustomEventType` with the same name for the custom and browser events was almost always a mistake that produced silently incorrect double-firing. The new validation surfaces the mistake at registration time. For more information, see [dotnet/aspnetcore#64774](https://github.com/dotnet/aspnetcore/pull/64774). + +## Recommended action + +If your code calls `Blazor.registerCustomEventType` with a custom name that's the same as the underlying browser event, rename the custom event to anything else (a common convention is to add a prefix such as `custom` or your component-library name): + +```javascript +Blazor.registerCustomEventType('lib-scroll', { + browserEventName: 'scrolltop' +}); +``` + +Then update the corresponding `[EventHandler]` attribute or `@on*` directive on the .NET side to use the new name. + +## Affected APIs + +None. `Blazor.registerCustomEventType` is a JavaScript API; it isn't surfaced through the .NET object model. For more information, see [custom events in Blazor](/aspnet/core/blazor/components/event-handling#custom-event-arguments). diff --git a/aspnetcore/breaking-changes/11/blazor-enhanced-nav-preloading-disabled.md b/aspnetcore/breaking-changes/11/blazor-enhanced-nav-preloading-disabled.md new file mode 100644 index 000000000000..fdadcdc2cad9 --- /dev/null +++ b/aspnetcore/breaking-changes/11/blazor-enhanced-nav-preloading-disabled.md @@ -0,0 +1,50 @@ +--- +title: "Breaking change: Blazor enhanced navigation no longer preloads resources" +description: "Learn about the breaking change in ASP.NET Core 11 where Blazor's ResourcePreloader no longer emits preload link hints for pages reached through enhanced navigation." +ms.date: 06/04/2026 +ai-usage: ai-assisted +--- + +# Blazor enhanced navigation no longer preloads resources + +Blazor no longer emits `` or `` resource hints for pages reached through enhanced navigation. Resource preloading still works for the initial page load and for normal (non-enhanced) navigation. + +## Version introduced + +.NET 11 + +## Previous behavior + +Previously, Blazor's `ResourcePreloader` emitted preload `` elements for resources used by the destination page on every server-rendered request, including requests served as an enhanced navigation. + +When an enhanced navigation occurred, the new preload elements were merged into the existing DOM. The merge often added and removed `` elements in a different order from the prior page, which caused some browsers to issue more preload requests than needed. The Blazor WebAssembly runtime also doesn't restart on an enhanced navigation, so preload hints emitted for it after the first page were redundant. + +## New behavior + +Starting in ASP.NET Core 11, `ResourcePreloader` only emits preload elements when the response isn't an enhanced navigation. Pages reached by enhanced navigation no longer receive the implicit preload hints that the framework added before. + +The previous behavior on the initial page load (the first request, before any enhanced navigation occurs) is unchanged. Pages that the user navigates to without enhanced navigation (for example, full-page reloads or external navigations) also still receive preload hints. + +## Type of breaking change + +This change is a [behavioral change](/dotnet/core/compatibility/categories#behavioral-change). + +## Reason for change + +The implicit preload hints during enhanced navigation produced redundant browser preload requests because the WebAssembly runtime doesn't restart and the DOM-merge step often produced an unstable order of `` elements. Removing the hints for enhanced navigation gives more predictable network behavior. For more information, see [dotnet/aspnetcore#63544](https://github.com/dotnet/aspnetcore/pull/63544). + +## Recommended action + +If you relied on the implicit preload hints during enhanced navigation, add explicit `` or `` tags in your `` for the specific resources you want to preload: + +```razor + + + +``` + +Alternatively, opt the relevant links out of enhanced navigation if preloading is more important to you than the snappier in-place navigation. For more information, see [enhanced navigation and form handling](/aspnet/core/blazor/fundamentals/routing#enhanced-navigation-and-form-handling). + +## Affected APIs + +None. No public API surface changed. The change affects the implicit resource preloading performed by Blazor server-side rendering. diff --git a/aspnetcore/breaking-changes/11/blazor-obsolete-apis-removed.md b/aspnetcore/breaking-changes/11/blazor-obsolete-apis-removed.md new file mode 100644 index 000000000000..32996c4f0dfd --- /dev/null +++ b/aspnetcore/breaking-changes/11/blazor-obsolete-apis-removed.md @@ -0,0 +1,89 @@ +--- +title: "Breaking change: Obsolete Blazor APIs removed" +description: "Learn about the breaking change in ASP.NET Core 11 where APIs that were marked obsolete in earlier Blazor releases have been removed." +ms.date: 06/04/2026 +ai-usage: ai-assisted +--- + +# Obsolete Blazor APIs removed + +A set of APIs across the Blazor Components assemblies that were previously marked obsolete have been removed in ASP.NET Core 11. Most of these APIs were marked obsolete several releases ago and have working replacements that have been recommended for years. + +## Version introduced + +.NET 11 + +## Previous behavior + +Previously, the affected APIs compiled with an obsolete warning. Most of the warnings pointed at a replacement API. For example, calling `EditContext.AddDataAnnotationsValidation()` produced an obsolete warning recommending `EnableDataAnnotationsValidation`. + +## New behavior + +In ASP.NET Core 11, the following APIs are removed and no longer compile. + +### Microsoft.AspNetCore.Components + +- `Router.PreferExactMatches` property. The property has had no effect since exact-match routing became the default in .NET 6. +- `ResourceAsset(string url)` constructor overload. + +### Microsoft.AspNetCore.Components.Forms + +- `EditContextDataAnnotationsExtensions.AddDataAnnotationsValidation(EditContext)` extension method. +- `EditContextDataAnnotationsExtensions.EnableDataAnnotationsValidation(EditContext)` overload (the overload without an `IServiceProvider` parameter). +- `RemoteBrowserFileStreamOptions` class. + +### Microsoft.AspNetCore.Components.Web + +- `WebEventCallbackFactoryEventArgsExtensions` class and all of its extension methods. +- The `init` accessor on `WebRenderer.RendererId`. The property is now set only via constructor. + +### Microsoft.AspNetCore.Components.WebAssembly + +- `JSInteropMethods.NotifyLocationChanged(string, bool)` method. + +### Microsoft.AspNetCore.Components.WebAssembly.Authentication + +- `SignOutSessionStateManager` class. The class has been a no-op since .NET 7. +- `RemoteAuthenticationService` constructor overload that doesn't take a logger. +- `AccessTokenResult` constructor and the `RedirectUrl` property. +- `AccessTokenNotAvailableException` now uses `InteractiveRequestUrl` in place of the removed `RedirectUrl`. + +## Type of breaking change + +This change can affect [source compatibility](/dotnet/core/compatibility/categories#source-compatibility). + +## Reason for change + +These APIs were marked obsolete in earlier releases (most between .NET 5 and .NET 8). Each had a documented replacement, and continuing to ship the obsolete surface area increased maintenance cost and surface area for Blazor without benefit. For more information, see [dotnet/aspnetcore#62755](https://github.com/dotnet/aspnetcore/pull/62755). + +## Recommended action + +Update your code to use the replacement APIs as listed in the following table. + +| Removed API | Replacement | +|-------------|-------------| +| `Router.PreferExactMatches` | Remove the assignment. Exact matching has been the default since .NET 6. | +| `ResourceAsset(string url)` | `ResourceAsset(string url, IReadOnlyList? properties)` | +| `EditContextDataAnnotationsExtensions.AddDataAnnotationsValidation(EditContext)` | `EditContextDataAnnotationsExtensions.EnableDataAnnotationsValidation(EditContext, IServiceProvider)` | +| `EditContextDataAnnotationsExtensions.EnableDataAnnotationsValidation(EditContext)` | `EditContextDataAnnotationsExtensions.EnableDataAnnotationsValidation(EditContext, IServiceProvider)` | +| `RemoteBrowserFileStreamOptions` | `BrowserFileStreamOptions` | +| `WebEventCallbackFactoryEventArgsExtensions` | Use `EventCallbackFactory` directly. | +| `WebRenderer.RendererId.init` accessor | Pass `RendererId` via the `WebRenderer` constructor. | +| `JSInteropMethods.NotifyLocationChanged(string, bool)` | `JSInteropMethods.NotifyLocationChanged(string uri, string? state, bool isInterceptedLink)` (added in .NET 7). This method is for framework use only. | +| `SignOutSessionStateManager` | Delete usages. The class has been a no-op since .NET 7. | +| `RemoteAuthenticationService` (constructor without logger) | The remaining constructor takes an `ILogger>` parameter. | +| `AccessTokenResult` constructor and `RedirectUrl` property | Use the remaining constructor and `InteractiveRequestUrl`. | + +## Affected APIs + +- +- +- +- `Microsoft.AspNetCore.Components.Forms.RemoteBrowserFileStreamOptions` +- `Microsoft.AspNetCore.Components.Web.WebEventCallbackFactoryEventArgsExtensions` +- `Microsoft.AspNetCore.Components.RenderTree.WebRenderer.RendererId` (init accessor) +- `Microsoft.AspNetCore.Components.WebAssembly.Infrastructure.JSInteropMethods.NotifyLocationChanged(System.String, System.Boolean)` +- `Microsoft.AspNetCore.Components.WebAssembly.Authentication.SignOutSessionStateManager` +- `Microsoft.AspNetCore.Components.WebAssembly.Authentication.RemoteAuthenticationService` (constructor overload without logger) +- `Microsoft.AspNetCore.Components.WebAssembly.Authentication.AccessTokenResult` (legacy constructor and `RedirectUrl` property) +- `Microsoft.AspNetCore.Components.ResourceAsset` (constructor overload without `properties`) diff --git a/aspnetcore/breaking-changes/11/concurrencylimiter-removed.md b/aspnetcore/breaking-changes/11/concurrencylimiter-removed.md new file mode 100644 index 000000000000..9e08343b1961 --- /dev/null +++ b/aspnetcore/breaking-changes/11/concurrencylimiter-removed.md @@ -0,0 +1,101 @@ +--- +title: "Breaking change: ConcurrencyLimiter middleware removed" +description: "Learn about the breaking change in ASP.NET Core 11 where the Microsoft.AspNetCore.ConcurrencyLimiter package and middleware are removed. Use the rate-limiting middleware instead." +ms.date: 06/04/2026 +ai-usage: ai-assisted +--- + +# ConcurrencyLimiter middleware removed + +The `Microsoft.AspNetCore.ConcurrencyLimiter` package and its middleware have been removed from ASP.NET Core 11. The middleware was marked obsolete in ASP.NET Core 8 in favor of the rate-limiting middleware (`Microsoft.AspNetCore.RateLimiting`), which exposes the equivalent concurrency-limiting functionality through `System.Threading.RateLimiting`. + +## Version introduced + +.NET 11 Preview 1 + +## Previous behavior + +Previously, you could install the [`Microsoft.AspNetCore.ConcurrencyLimiter`](https://www.nuget.org/packages/Microsoft.AspNetCore.ConcurrencyLimiter) NuGet package and use the `UseConcurrencyLimiter` extension method together with one of the queue policies (`AddQueuePolicy` or `AddStackPolicy`) to bound how many requests could run concurrently: + +```csharp +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.ConcurrencyLimiter; +using Microsoft.Extensions.DependencyInjection; + +var builder = WebApplication.CreateBuilder(args); + +builder.Services.AddQueuePolicy(options => +{ + options.MaxConcurrentRequests = 2; + options.RequestQueueLimit = 25; +}); + +var app = builder.Build(); + +app.UseConcurrencyLimiter(); +app.Run(async context => await context.Response.WriteAsync("Hello World!")); +app.Run(); +``` + +The middleware emitted a build warning that directed you to the rate-limiting middleware, but it still worked at runtime. + +## New behavior + +Starting in ASP.NET Core 11, the `Microsoft.AspNetCore.ConcurrencyLimiter` middleware source is removed from the ASP.NET Core repository, and no `Microsoft.AspNetCore.ConcurrencyLimiter` 11.x or later package is published. The following types and methods are no longer available to apps that target `net11.0`: + +- `Microsoft.AspNetCore.ConcurrencyLimiter.ConcurrencyLimiterMiddleware` +- `Microsoft.AspNetCore.ConcurrencyLimiter.ConcurrencyLimiterOptions` +- `Microsoft.AspNetCore.ConcurrencyLimiter.IQueuePolicy` +- `Microsoft.AspNetCore.ConcurrencyLimiter.QueuePolicyOptions` +- `Microsoft.AspNetCore.Builder.ConcurrencyLimiterExtensions` (including the `UseConcurrencyLimiter` extension method) +- `Microsoft.Extensions.DependencyInjection.QueuePolicyServiceCollectionExtensions` (including `AddQueuePolicy` and `AddStackPolicy`) + +Existing 9.x and 10.x package versions remain on NuGet and continue to work for projects that still target `net9.0` or `net10.0`. Projects that take an explicit `PackageReference` and retarget to `net11.0` get a NuGet restore error because there's no 11.x package version. Code that references these types fails to compile against ASP.NET Core 11. + +## Type of breaking change + +This change can affect [binary compatibility](/dotnet/core/compatibility/categories#binary-compatibility) and [source compatibility](/dotnet/core/compatibility/categories#source-compatibility). + +## Reason for change + +The `ConcurrencyLimiter` middleware was marked obsolete in ASP.NET Core 8 (diagnostic `ASP0025`) because its functionality is covered by the rate-limiting middleware, which is built on the more general-purpose `System.Threading.RateLimiting` APIs. Maintaining two parallel implementations adds friction without benefit. For more information, see [dotnet/aspnetcore#64020](https://github.com/dotnet/aspnetcore/pull/64020). + +## Recommended action + +Migrate to the [rate-limiting middleware](/aspnet/core/performance/rate-limit), which provides an equivalent concurrency limiter built on : + +```csharp +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.DependencyInjection; +using System.Threading.RateLimiting; + +var builder = WebApplication.CreateBuilder(args); + +builder.Services.AddRateLimiter(options => +{ + options.AddConcurrencyLimiter("ConcurrencyPolicy", limiterOptions => + { + limiterOptions.PermitLimit = 2; + limiterOptions.QueueLimit = 25; + limiterOptions.QueueProcessingOrder = QueueProcessingOrder.OldestFirst; + }); +}); + +var app = builder.Build(); + +app.UseRateLimiter(); +app.MapGet("/", () => "Hello World!").RequireRateLimiting("ConcurrencyPolicy"); +app.Run(); +``` + +If you can't migrate immediately, you can stay on a `Microsoft.AspNetCore.ConcurrencyLimiter` 9.x or 10.x [NuGet package](https://www.nuget.org/packages/Microsoft.AspNetCore.ConcurrencyLimiter) while you target `net11.0`. No new versions of the package will ship for .NET 11 or later. + +## Affected APIs + +- `Microsoft.AspNetCore.ConcurrencyLimiter.ConcurrencyLimiterMiddleware` +- `Microsoft.AspNetCore.ConcurrencyLimiter.ConcurrencyLimiterOptions` +- `Microsoft.AspNetCore.ConcurrencyLimiter.IQueuePolicy` +- `Microsoft.AspNetCore.ConcurrencyLimiter.QueuePolicyOptions` +- `Microsoft.AspNetCore.Builder.ConcurrencyLimiterExtensions.UseConcurrencyLimiter` +- `Microsoft.Extensions.DependencyInjection.QueuePolicyServiceCollectionExtensions.AddQueuePolicy` +- `Microsoft.Extensions.DependencyInjection.QueuePolicyServiceCollectionExtensions.AddStackPolicy` diff --git a/aspnetcore/breaking-changes/11/http-activity-otel-semconv.md b/aspnetcore/breaking-changes/11/http-activity-otel-semconv.md new file mode 100644 index 000000000000..20c7099dad4d --- /dev/null +++ b/aspnetcore/breaking-changes/11/http-activity-otel-semconv.md @@ -0,0 +1,63 @@ +--- +title: "Breaking change: ASP.NET Core hosting emits OpenTelemetry HTTP semantic-convention tags by default" +description: "Learn about the breaking change in ASP.NET Core 11 where the HTTP server Activity emits OpenTelemetry HTTP semantic-convention tags by default, the http.route tag uses literal default values for conventional routes, and Activity.StatusDescription is no longer set on exceptions." +ms.date: 06/04/2026 +ai-usage: ai-assisted +--- + +# ASP.NET Core hosting emits OpenTelemetry HTTP semantic-convention tags by default + +The HTTP server created by `Microsoft.AspNetCore.Hosting` now emits the OpenTelemetry HTTP server-span semantic-convention tags by default, and its `http.route` tag uses the resolved values of conventional-route default parameters. is no longer set when an unhandled exception occurs. Together, these changes bring the built-in `Microsoft.AspNetCore.Hosting` activity into line with the metadata produced by `OpenTelemetry.Instrumentation.AspNetCore`. + +## Version introduced + +.NET 11 + +## Previous behavior + +Previously: + +- The `Microsoft.AspNetCore.Hosting.SuppressActivityOpenTelemetryData` `AppContext` switch defaulted to `true`, so the framework's HTTP server `Activity` didn't carry OpenTelemetry HTTP server-span tags. Apps that wanted those tags used `OpenTelemetry.Instrumentation.AspNetCore` to add them, or set the switch to `false` explicitly. +- The `http.route` tag on the activity used the raw route template. For conventional routes such as `{controller=Home}/{action=Index}/{id?}`, every endpoint that resolved through the conventional pipeline reported the same `http.route` value. +- When an unhandled exception escaped the request pipeline, the framework set `Activity.Status` to `Error` and also set `Activity.StatusDescription` to the exception message. + +## New behavior + +Starting in ASP.NET Core 11: + +- `Microsoft.AspNetCore.Hosting.SuppressActivityOpenTelemetryData` defaults to `false`. The framework's HTTP server `Activity` now carries the OpenTelemetry HTTP [server-span](https://opentelemetry.io/docs/specs/semconv/http/http-spans/#http-server-span) tags out of the box. The added tags include `http.request.method`, `http.response.status_code`, `http.route`, `network.protocol.version`, `url.path`, `url.scheme`, `server.address`, `server.port`, and `user_agent.original`. See the [OpenTelemetry HTTP server span spec](https://opentelemetry.io/docs/specs/semconv/http/http-spans/#http-server-span) for the full set of attributes. +- The HTTP request duration metric (`http.server.request.duration`) carries an additional `error.type` dimension for `5xx` responses. +- For conventional routes, the `http.route` tag has the `{controller}`, `{action}`, `{area}`, and `{page}` parameters replaced by their resolved values. For example, a request to `Store/Checkout` matched by the conventional route `{controller=Home}/{action=Index}/{id?}` now reports `http.route = "Store/Checkout/{id?}"` instead of `"{controller=Home}/{action=Index}/{id?}"`. +- When an unhandled exception escapes the request pipeline, the framework sets `Activity.Status` to `Error` but does *not* set `Activity.StatusDescription`. The exception is still recorded as an `Activity` event with the standard `exception.type`, `exception.message`, and `exception.stacktrace` tags, and the exception type is reflected in the `error.type` metric dimension. + +## Type of breaking change + +This is a [behavioral change](/dotnet/core/compatibility/categories#behavioral-change). + +## Reason for change + +Aligning the built-in hosting activity with the OpenTelemetry HTTP server-span specification lets apps observe their HTTP behavior without taking an extra dependency on `OpenTelemetry.Instrumentation.AspNetCore`. Using resolved route values in `http.route` matches the OpenTelemetry guidance for that tag and produces more useful aggregations in dashboards. Not setting `Activity.StatusDescription` on exceptions aligns with `OpenTelemetry.Instrumentation.AspNetCore` (which already left it unset) and avoids putting potentially sensitive exception messages in places that aren't designed to receive them. + +For more information, see [dotnet/aspnetcore#64851](https://github.com/dotnet/aspnetcore/pull/64851), [dotnet/aspnetcore#64854](https://github.com/dotnet/aspnetcore/pull/64854), and [dotnet/aspnetcore#65825](https://github.com/dotnet/aspnetcore/pull/65825). + +## Recommended action + +Most apps don't need to do anything — the new defaults match what `OpenTelemetry.Instrumentation.AspNetCore` already produced. + +If you observe **duplicate tags** because both the framework and `OpenTelemetry.Instrumentation.AspNetCore` add the same set, upgrade the instrumentation package to the latest version, which detects the built-in tags and skips adding them. + +If you have a custom span enricher or exporter that depended on the *absence* of these tags, on the previous `http.route` format, or on `Activity.StatusDescription` being populated, update it. The `error.type` metric dimension also grows the cardinality of the duration metric for 5xx responses; review your metrics backend's cardinality budget if you previously aggregated by status code only. + +To restore the pre-11 behavior (no OpenTelemetry semantic-convention tags from the framework), set the `AppContext` switch back to `true` early in app startup: + +```csharp +AppContext.SetSwitch("Microsoft.AspNetCore.Hosting.SuppressActivityOpenTelemetryData", true); +``` + +The switch only controls the OpenTelemetry tags. The `http.route` value-substitution change and the `Activity.StatusDescription` change can't be reverted independently. + +## Affected APIs + +- +- +- The OpenTelemetry [HTTP server span semantic conventions](https://opentelemetry.io/docs/specs/semconv/http/http-spans/#http-server-span). diff --git a/aspnetcore/breaking-changes/11/kestrel-strict-protocol-compliance.md b/aspnetcore/breaking-changes/11/kestrel-strict-protocol-compliance.md new file mode 100644 index 000000000000..94dc0ac9b747 --- /dev/null +++ b/aspnetcore/breaking-changes/11/kestrel-strict-protocol-compliance.md @@ -0,0 +1,60 @@ +--- +title: "Breaking change: Kestrel tightens HTTP protocol compliance" +description: "Learn about the breaking change in ASP.NET Core 11 where Kestrel rejects additional HTTP/2 and HTTP/3 connection-specific headers and closes HTTP/1.1 connections after a request with both Content-Length and Transfer-Encoding." +ms.date: 06/04/2026 +ai-usage: ai-assisted +--- + +# Kestrel tightens HTTP protocol compliance + +Kestrel in ASP.NET Core 11 makes two HTTP-protocol-compliance changes: + +- HTTP/2 and HTTP/3 reject more connection-specific request headers, per RFC 9113 §8.2.2 and RFC 9114 §4.2. +- HTTP/1.1 closes the connection after a request that included both `Content-Length` and `Transfer-Encoding`, per the updated wording of RFC 9112. + +Both changes are stricter conformance to the HTTP specifications. Compliant clients are unaffected. Misbehaving clients or older proxies that don't fix up these headers might see new errors or extra connection re-handshakes. + +## Version introduced + +.NET 11 + +## Previous behavior + +**HTTP/2 and HTTP/3.** Kestrel rejected requests that included the `Connection` or `TE` header (where `TE` had a value other than `trailers`), but it accepted requests that included the other connection-specific headers called out by the RFCs: `Proxy-Connection`, `Keep-Alive`, `Transfer-Encoding`, and `Upgrade`. + +**HTTP/1.1.** When a request included both `Content-Length` and `Transfer-Encoding` headers, Kestrel ignored `Content-Length`, honored `Transfer-Encoding: chunked`, processed the request, and then kept the connection alive for subsequent requests. + +## New behavior + +**HTTP/2 and HTTP/3.** Kestrel now treats a request as malformed and resets the stream (HTTP/2) or fails the request (HTTP/3) if it includes any of the following connection-specific headers: + +- `Connection` +- `Proxy-Connection` +- `Keep-Alive` +- `TE` (with a value other than `trailers`) +- `Transfer-Encoding` +- `Upgrade` + +**HTTP/1.1.** When a request includes both `Content-Length` and `Transfer-Encoding`, Kestrel still strips `Content-Length` and processes the request, but it now closes the connection after sending the response instead of keeping it alive. This mitigates a known HTTP request-smuggling vector. + +## Type of breaking change + +These changes are [behavioral changes](/dotnet/core/compatibility/categories#behavioral-change). + +## Reason for change + +Per RFC 9113 §8.2.2 (HTTP/2) and RFC 9114 §4.2 (HTTP/3), endpoints must treat any message that contains connection-specific header fields as malformed. The previous Kestrel implementation only rejected `Connection` and `TE`, leaving four other RFC-listed headers unenforced. + +Per the updated RFC 9112, a server must close the connection after processing a request that contained both `Content-Length` and `Transfer-Encoding`. Closing the connection prevents a class of HTTP request-smuggling attacks that exploit the ambiguity between the two framing headers. + +For more information, see [dotnet/aspnetcore#66669](https://github.com/dotnet/aspnetcore/pull/66669) and [dotnet/aspnetcore#66671](https://github.com/dotnet/aspnetcore/pull/66671). + +## Recommended action + +For the HTTP/2 and HTTP/3 change, audit any clients, gateways, or HTTP/1.1-to-HTTP/2 translating proxies that forward requests to Kestrel and make sure they strip the connection-specific headers when upgrading the protocol. Compliant HTTP/2 and HTTP/3 clients don't send these headers. + +For the HTTP/1.1 change, audit any client that emits both `Content-Length` and `Transfer-Encoding` and update it to send only one. Sending only `Transfer-Encoding: chunked` (and omitting `Content-Length`) is the simplest fix. Clients that send only `Content-Length` are unaffected. + +## Affected APIs + +None directly. The change is enforced at the protocol layer by Kestrel; no public API surface is changed. For more information, see the [Kestrel documentation](/aspnet/core/fundamentals/servers/kestrel). diff --git a/aspnetcore/breaking-changes/11/microsoft-openapi-3x.md b/aspnetcore/breaking-changes/11/microsoft-openapi-3x.md new file mode 100644 index 000000000000..02a5db573f85 --- /dev/null +++ b/aspnetcore/breaking-changes/11/microsoft-openapi-3x.md @@ -0,0 +1,71 @@ +--- +title: "Breaking change: Microsoft.OpenApi upgraded to 3.x" +description: "Learn about the breaking change in ASP.NET Core 11 where Microsoft.AspNetCore.OpenApi takes a dependency on Microsoft.OpenApi 3.x. Document and operation transformers might need updates." +ms.date: 06/04/2026 +ai-usage: ai-assisted +--- + +# Microsoft.OpenApi upgraded to 3.x + +`Microsoft.AspNetCore.OpenApi` in ASP.NET Core 11 takes a dependency on `Microsoft.OpenApi` 3.x (currently 3.6.0). The previous release (.NET 10) depended on `Microsoft.OpenApi` 2.x. This is a major-version bump of a transitive dependency, and several types that document and operation transformers receive have changed shape. + +## Version introduced + +.NET 11 Preview 3 + +## Previous behavior + +Previously, `Microsoft.AspNetCore.OpenApi` depended on `Microsoft.OpenApi` 2.x. Implementations of the document, operation, and schema transformer interfaces worked against the 2.x object model — for example, against the `OpenApiSchema` shape that exposed nested schemas, references, extensions, and parsing helpers as 2.x concrete types: + +```csharp +using Microsoft.AspNetCore.OpenApi; +using Microsoft.OpenApi.Any; +using Microsoft.OpenApi.Models; + +builder.Services.AddOpenApi(options => +{ + options.AddSchemaTransformer((schema, context, ct) => + { + // Microsoft.OpenApi 2.x: OpenApiString was a concrete type in Microsoft.OpenApi.Any. + schema.Extensions["x-schema-id"] = new OpenApiString(context.JsonTypeInfo.Type.Name); + return Task.CompletedTask; + }); +}); +``` + +## New behavior + +Starting in ASP.NET Core 11, `Microsoft.AspNetCore.OpenApi` depends on `Microsoft.OpenApi` 3.x. The `IOpenApiDocumentTransformer`, `IOpenApiOperationTransformer`, and `IOpenApiSchemaTransformer` interface signatures still expose the concrete `OpenApiDocument`, `OpenApiOperation`, and `OpenApiSchema` parameter types, but the surface area of those types has changed: + +- Several formerly concrete types that nested types expose are now interfaces (for example, `IOpenApiSchema`, `IOpenApiAny`). +- The 2.x `OpenApiAny` types (such as `OpenApiString`, `OpenApiInteger`) were removed in favor of -based extension values. +- The `ParseNode` parsing infrastructure was removed in favor of visitor- and writer-based APIs. +- The `OpenApiReference` model was reshaped to better distinguish between local and external references. + +Transformer code that does anything more than read-only inspection — sets extension values, walks nested schemas, or constructs new schemas — usually needs to be updated. + +## Type of breaking change + +This change can affect [source compatibility](/dotnet/core/compatibility/categories#source-compatibility), and also [binary compatibility](/dotnet/core/compatibility/categories#binary-compatibility) for libraries that were compiled against `Microsoft.OpenApi` 2.x and are loaded into an app that resolves the 3.x assembly at runtime. + +## Reason for change + +The new `Microsoft.OpenApi` 3.x release adds support for OpenAPI 3.1.0 and 3.2.0 specifications, fixes long-standing issues in the object model, and adopts interface-based abstractions that are easier to extend. For more information, see [dotnet/aspnetcore#65415](https://github.com/dotnet/aspnetcore/pull/65415) and [dotnet/aspnetcore#66998](https://github.com/dotnet/aspnetcore/pull/66998). + +## Recommended action + +Update any custom document, operation, or schema transformer to compile against `Microsoft.OpenApi` 3.x. The most common changes are: + +- Replace 2.x `OpenApiAny` values such as `new OpenApiString("...")` with `JsonNode` values (`JsonValue.Create("...")`). +- Update code that walks nested schemas or references to handle the new interface-based abstractions (for example, `IOpenApiSchema`). +- Update code that used the removed `ParseNode` parsing infrastructure to use the new visitor- and writer-based APIs. +- Review use of `OpenApiReference`, which was reshaped to better distinguish between local and external references. + +For the canonical migration notes for the 3.0, 3.1, 3.2, and later releases, see the [OpenAPI.NET release notes](https://github.com/microsoft/OpenAPI.NET/releases). + +## Affected APIs + +- +- +- +- diff --git a/aspnetcore/breaking-changes/11/openapi-server-url-trailing-slash.md b/aspnetcore/breaking-changes/11/openapi-server-url-trailing-slash.md new file mode 100644 index 000000000000..36a5fe227dc7 --- /dev/null +++ b/aspnetcore/breaking-changes/11/openapi-server-url-trailing-slash.md @@ -0,0 +1,76 @@ +--- +title: "Breaking change: OpenAPI server URL no longer has a trailing slash when PathBase is empty" +description: "Learn about the breaking change in ASP.NET Core 11 where the generated OpenAPI document's servers[0].url no longer ends with a trailing slash when the request PathBase is empty." +ms.date: 06/04/2026 +ai-usage: ai-assisted +--- + +# OpenAPI server URL no longer has a trailing slash when PathBase is empty + +The server URL that ASP.NET Core writes into the generated OpenAPI document no longer ends with a trailing slash when the request is empty. Tooling or contract tests that string-compare `servers[0].url` need to be updated. + +## Version introduced + +.NET 11 + +## Previous behavior + +Previously, `OpenApiDocumentService.GetOpenApiServers()` used `UriHelper.BuildAbsolute()`, which appends a trailing slash when both `pathBase` and `path` are empty. The generated OpenAPI document contained a `servers` entry with a trailing slash: + +```json +{ + "servers": [ + { "url": "https://example.com/" } + ] +} +``` + +## New behavior + +Starting in ASP.NET Core 11, the trailing slash is stripped when `HttpRequest.PathBase.HasValue` is `false`. The generated OpenAPI document no longer includes the trailing slash: + +```json +{ + "servers": [ + { "url": "https://example.com" } + ] +} +``` + +If `PathBase` has an explicit value (for example, `/api`), the URL is unchanged and continues to include the path base. + +## Type of breaking change + +This change is a [behavioral change](/dotnet/core/compatibility/categories#behavioral-change). + +## Reason for change + +The previous behavior didn't match the OpenAPI 3.1.0 specification examples, which use server URLs without a trailing slash. The change aligns the generated document with the spec and avoids surprises in downstream tooling that's sensitive to the difference. For more information, see [dotnet/aspnetcore#64716](https://github.com/dotnet/aspnetcore/pull/64716). + +## Recommended action + +If your build pipeline or contract tests compare the generated OpenAPI document against a fixture, update the fixture to remove the trailing slash. If you use a code generator or SDK that constructs request URIs by concatenating the server URL with operation paths, verify that it produces the same URLs as before. Most generators normalize repeated slashes, but a few don't. + +If you intentionally want the trailing slash (for example, to match an existing fixture), use an OpenAPI document transformer to write it back: + +```csharp +builder.Services.AddOpenApi(options => +{ + options.AddDocumentTransformer((document, context, ct) => + { + foreach (var server in document.Servers ?? []) + { + if (!server.Url.EndsWith('/')) + { + server.Url += "/"; + } + } + return Task.CompletedTask; + }); +}); +``` + +## Affected APIs + +- +- Generated `servers[0].url` in the OpenAPI document produced by [`AddOpenApi`](/aspnet/core/fundamentals/openapi/aspnetcore-openapi). diff --git a/aspnetcore/breaking-changes/11/overview.md b/aspnetcore/breaking-changes/11/overview.md new file mode 100644 index 000000000000..274d3c2f2427 --- /dev/null +++ b/aspnetcore/breaking-changes/11/overview.md @@ -0,0 +1,28 @@ +--- +title: Breaking changes in ASP.NET Core 11 +titleSuffix: "" +description: Navigate to the breaking changes in ASP.NET Core 11. +ms.date: 06/04/2026 +ai-usage: ai-assisted +no-loc: [Blazor, Kestrel, SignalR] +--- +# Breaking changes in ASP.NET Core 11 + +If you're migrating an app to ASP.NET Core 11, the breaking changes listed here might affect you. + +[!INCLUDE [binary-source-behavioral](../includes/binary-source-behavioral.md)] + +| Title | Type of change | +|-------|-------------------| +| [Blazor custom event registration throws when name matches a browser event](blazor-custom-event-name-collision.md) | Behavioral change | +| [Blazor enhanced navigation no longer preloads resources](blazor-enhanced-nav-preloading-disabled.md) | Behavioral change | +| [ConcurrencyLimiter middleware removed](concurrencylimiter-removed.md) | Binary/source incompatible | +| [Hosting emits OpenTelemetry HTTP semantic-convention tags by default](http-activity-otel-semconv.md) | Behavioral change | +| [Kestrel tightens HTTP protocol compliance](kestrel-strict-protocol-compliance.md) | Behavioral change | +| [Microsoft.OpenApi upgraded to 3.x](microsoft-openapi-3x.md) | Source incompatible | +| [Obsolete Blazor APIs removed](blazor-obsolete-apis-removed.md) | Source incompatible | +| [OpenAPI server URL no longer has a trailing slash when PathBase is empty](openapi-server-url-trailing-slash.md) | Behavioral change | +| [Passkey sign-in enforces email/phone confirmation and lockout](passkey-signin-enforces-confirmation-lockout.md) | Behavioral change | +| [Response compression always emits Vary: Accept-Encoding](response-compression-always-vary.md) | Behavioral change | +| [SqlClient Active Directory authentication moved to a separate package](sqlclient-azure-extensions-required.md) | Behavioral change | +| [WebAssemblyHostBuilder loads environment variables into IConfiguration](wasm-env-vars-in-configuration.md) | Behavioral change | diff --git a/aspnetcore/breaking-changes/11/passkey-signin-enforces-confirmation-lockout.md b/aspnetcore/breaking-changes/11/passkey-signin-enforces-confirmation-lockout.md new file mode 100644 index 000000000000..cc79cc175c69 --- /dev/null +++ b/aspnetcore/breaking-changes/11/passkey-signin-enforces-confirmation-lockout.md @@ -0,0 +1,53 @@ +--- +title: "Breaking change: Passkey sign-in enforces email/phone confirmation and lockout" +description: "Learn about the breaking change in ASP.NET Core 11 where PasskeySignInAsync calls PreSignInCheck so that confirmed-email, confirmed-phone, and lockout requirements are honored." +ms.date: 06/04/2026 +ai-usage: ai-assisted +--- + +# Passkey sign-in enforces email/phone confirmation and lockout + +`SignInManager.PasskeySignInAsync` now calls `PreSignInCheck` before signing the user in. This means a successful passkey assertion alone is no longer enough to sign in if the user's account is locked out or hasn't met the configured `RequireConfirmedEmail`, `RequireConfirmedPhoneNumber`, or `RequireConfirmedAccount` policies. This aligns passkey sign-in behavior with `PasswordSignInAsync` and the other sign-in methods. + +## Version introduced + +.NET 11 + +## Previous behavior + +Previously, `SignInManager.PasskeySignInAsync` didn't call `PreSignInCheck`. A successful passkey assertion signed the user in even when: + +- `SignInOptions.RequireConfirmedEmail` was `true` and the user's email wasn't confirmed. +- `SignInOptions.RequireConfirmedPhoneNumber` was `true` and the user's phone number wasn't confirmed. +- `SignInOptions.RequireConfirmedAccount` was `true` and the user's account wasn't confirmed. +- The user was locked out (their `LockoutEnd` was in the future). + +This was inconsistent with `PasswordSignInAsync` and other sign-in methods, which already enforced these requirements. + +## New behavior + +Starting in ASP.NET Core 11, after a successful passkey assertion, `PasskeySignInAsync` calls `PreSignInCheck` before issuing the authentication cookie. If the user fails any of the configured checks, the method returns one of the following without signing the user in: + +- if the user is locked out. +- if the user's email, phone, or account isn't confirmed. + +## Type of breaking change + +This change is a [behavioral change](/dotnet/core/compatibility/categories#behavioral-change). + +## Reason for change + +`PasskeySignInAsync` was missing the `PreSignInCheck` call that the other `SignInManager` sign-in methods make. As a result, an application that required confirmed email or phone for password sign-in unintentionally let users bypass that requirement by enrolling a passkey. The change brings passkey sign-in into line with the rest of the sign-in surface. For more information, see [dotnet/aspnetcore#65024](https://github.com/dotnet/aspnetcore/pull/65024). + +## Recommended action + +Review your sign-in UI flow to handle `SignInResult.LockedOut` and `SignInResult.NotAllowed` for passkey sign-in the same way you already handle them for password sign-in. The scaffolded Identity UI in the ASP.NET Core templates already handles both results. + +If your app currently relies on passkey sign-in to bypass these checks, the safest change is to update the relevant user records (for example, mark the email as confirmed) rather than to remove the checks themselves. Removing the checks on `SignInManager.PreSignInCheck` affects every sign-in path, not only passkey sign-in. + +## Affected APIs + +- +- +- +- diff --git a/aspnetcore/breaking-changes/11/response-compression-always-vary.md b/aspnetcore/breaking-changes/11/response-compression-always-vary.md new file mode 100644 index 000000000000..eaebe0533262 --- /dev/null +++ b/aspnetcore/breaking-changes/11/response-compression-always-vary.md @@ -0,0 +1,61 @@ +--- +title: "Breaking change: Response compression always emits Vary: Accept-Encoding" +description: "Learn about the breaking change in ASP.NET Core 11 where the response compression middleware always appends Vary: Accept-Encoding to responses it sees, even when no compression was applied." +ms.date: 06/04/2026 +ai-usage: ai-assisted +--- + +# Response compression always emits Vary: Accept-Encoding + +The response compression middleware now appends `Vary: Accept-Encoding` to every response that passes through it, including responses for which it didn't actually compress the body. Downstream caches see more correct cache keys, at the cost of slightly more cache variants when caches don't normalize the `Accept-Encoding` request header. + +## Version introduced + +.NET 11 + +## Previous behavior + +Previously, the response compression middleware only appended `Vary: Accept-Encoding` to responses it actually compressed. Responses for which compression was skipped — for example, because the content type wasn't in `MimeTypes`, or because the client preferred `identity` encoding — were sent without the `Vary` header added by the middleware. + +A downstream cache could therefore serve a compressed response that was stored for `Accept-Encoding: gzip` to a client that only accepted `identity` (and vice versa), unless the cache or the origin already added the header by another means. + +## New behavior + +Starting in ASP.NET Core 11, the response compression middleware appends `Vary: Accept-Encoding` to the response header collection for every response it sees, whether or not it compressed the body. If the header already contains `Accept-Encoding`, the middleware doesn't add a duplicate token. + +## Type of breaking change + +This change is a [behavioral change](/dotnet/core/compatibility/categories#behavioral-change). + +## Reason for change + +Without the `Vary: Accept-Encoding` header on every response, a shared cache can serve a stored encoded response to a client that doesn't accept that encoding. Adding the header unconditionally makes the middleware's response correctly cacheable by HTTP-compliant intermediaries. For more information, see [dotnet/aspnetcore#55092](https://github.com/dotnet/aspnetcore/pull/55092) and the issue it fixes, [dotnet/aspnetcore#48008](https://github.com/dotnet/aspnetcore/issues/48008). + +## Recommended action + +No source change is required. Verify that any downstream cache (CDN, reverse proxy, browser cache) normalizes the `Accept-Encoding` request header so it doesn't store an unnecessarily large number of variants. Most modern CDNs already do this. + +If you don't want the middleware to add the header to a specific response, remove it after the middleware runs: + +```csharp +app.UseResponseCompression(); + +app.Use(async (context, next) => +{ + context.Response.OnStarting(() => + { + // Remove the Vary header added by UseResponseCompression for a specific path. + if (context.Request.Path.StartsWithSegments("/no-vary")) + { + context.Response.Headers.Remove("Vary"); + } + return Task.CompletedTask; + }); + await next(); +}); +``` + +## Affected APIs + +- +- diff --git a/aspnetcore/breaking-changes/11/sqlclient-azure-extensions-required.md b/aspnetcore/breaking-changes/11/sqlclient-azure-extensions-required.md new file mode 100644 index 000000000000..37b6a3bb964f --- /dev/null +++ b/aspnetcore/breaking-changes/11/sqlclient-azure-extensions-required.md @@ -0,0 +1,53 @@ +--- +title: "Breaking change: SqlClient Active Directory authentication moved to a separate package" +description: "Learn about the breaking change in ASP.NET Core 11 where Microsoft.Data.SqlClient 7.x extracts Active Directory authentication providers into the Microsoft.Data.SqlClient.Extensions.Azure package." +ms.date: 06/04/2026 +ai-usage: ai-assisted +--- + +# SqlClient Active Directory authentication moved to a separate package + +ASP.NET Core 11 transitively brings in `Microsoft.Data.SqlClient` 7.x (up from 5.x in .NET 10). Starting in `Microsoft.Data.SqlClient` 6.0, the Microsoft Entra ID (formerly Azure Active Directory) authentication providers are no longer in the main `Microsoft.Data.SqlClient` package; they ship in a separate [`Microsoft.Data.SqlClient.Extensions.Azure`](https://www.nuget.org/packages/Microsoft.Data.SqlClient.Extensions.Azure) package. Apps that use an Entra ID `Authentication=` value in their SQL connection string must add a reference to this package. + +## Version introduced + +.NET 11 Preview 3 + +## Previous behavior + +Previously, the `Microsoft.Data.SqlClient` 5.x package included the Entra ID authentication providers. A connection string such as `Server=...;Database=...;Authentication=Active Directory Default` worked out of the box for any ASP.NET Core app that transitively pulled in `Microsoft.Data.SqlClient` (for example, through `Microsoft.Extensions.Caching.SqlServer` or an Entity Framework Core SQL Server provider). + +## New behavior + +Starting in ASP.NET Core 11, the transitive `Microsoft.Data.SqlClient` version is 7.x, which no longer ships the Entra ID authentication providers. If your connection string uses one of the `Active Directory *` `Authentication=` values and you haven't added the `Microsoft.Data.SqlClient.Extensions.Azure` package, you get a runtime error similar to: + +```output +Cannot find an authentication provider for 'ActiveDirectoryDefault'. +Install the 'Microsoft.Data.SqlClient.Extensions.Azure' NuGet package to use Active Directory (Entra ID) authentication methods. +``` + +This affects apps that use `Microsoft.Extensions.Caching.SqlServer` (`DistributedSqlServerCache`), apps that use the EF Core SQL Server provider, and any other code path that transitively brings in `Microsoft.Data.SqlClient` and uses Entra ID authentication. + +## Type of breaking change + +This change is a [behavioral change](/dotnet/core/compatibility/categories#behavioral-change). + +## Reason for change + +The change to move Microsoft Entra ID authentication out of `Microsoft.Data.SqlClient` was made in the upstream `Microsoft.Data.SqlClient` 6.0 release. ASP.NET Core 11 picks up that change by upgrading the SqlClient version it depends on transitively. For more information, see [dotnet/aspnetcore#66179](https://github.com/dotnet/aspnetcore/pull/66179). + +## Recommended action + +If your app uses a SQL connection string with any `Active Directory *` `Authentication=` value, add a `PackageReference` to `Microsoft.Data.SqlClient.Extensions.Azure`: + +```xml + + + +``` + +The ASP.NET Core "Individual Accounts" project templates were updated to add this reference for the Azure SQL configurations they support. New projects don't require any manual change. + +## Affected APIs + +None. The break is a transitive runtime requirement to install a NuGet package. No types in `Microsoft.AspNetCore.*` are affected. diff --git a/aspnetcore/breaking-changes/11/wasm-env-vars-in-configuration.md b/aspnetcore/breaking-changes/11/wasm-env-vars-in-configuration.md new file mode 100644 index 000000000000..4debf8dd059e --- /dev/null +++ b/aspnetcore/breaking-changes/11/wasm-env-vars-in-configuration.md @@ -0,0 +1,67 @@ +--- +title: "Breaking change: WebAssemblyHostBuilder loads environment variables into IConfiguration" +description: "Learn about the breaking change in ASP.NET Core 11 where WebAssemblyHostBuilder.CreateDefault adds environment variables to IConfiguration by default." +ms.date: 06/04/2026 +ai-usage: ai-assisted +--- + +# WebAssemblyHostBuilder loads environment variables into IConfiguration + +`WebAssemblyHostBuilder.CreateDefault` now calls `AddEnvironmentVariables()` on the configuration builder. Environment variables that are passed into the Blazor WebAssembly runtime through `MonoConfig.environmentVariables` are now visible through `IConfiguration` in addition to . + +## Version introduced + +.NET 11 + +## Previous behavior + +Previously, environment variables that were available to a Blazor WebAssembly app through weren't loaded into `IConfiguration`. To read them through `IConfiguration`, you had to add the environment-variables provider manually: + +```csharp +var builder = WebAssemblyHostBuilder.CreateDefault(args); +// Required to read environment variables through IConfiguration. +builder.Configuration.AddEnvironmentVariables(); + +var endpoint = builder.Configuration["OTEL_EXPORTER_OTLP_ENDPOINT"]; +``` + +## New behavior + +Starting in ASP.NET Core 11, `WebAssemblyHostBuilder.CreateDefault` calls `AddEnvironmentVariables()` for you. Environment variables are visible in `IConfiguration` without any extra setup: + +```csharp +var builder = WebAssemblyHostBuilder.CreateDefault(args); + +// Works without an explicit AddEnvironmentVariables call. +var endpoint = builder.Configuration["OTEL_EXPORTER_OTLP_ENDPOINT"]; +``` + +This aligns the WebAssembly host with `WebApplication.CreateBuilder()`, which has loaded environment variables into `IConfiguration` since .NET 6. + +The configuration sources are still ordered so that environment variables override earlier sources (such as `appsettings.json`) and are overridden by later sources (such as command-line arguments). An environment variable with the same name as an `appsettings.json` key (for example, `Logging__LogLevel__Default`) now takes precedence over the JSON value. + +## Type of breaking change + +This change is a [behavioral change](/dotnet/core/compatibility/categories#behavioral-change). + +## Reason for change + +This change aligns the Blazor WebAssembly host with the server-side `WebApplication.CreateBuilder()` configuration sources and unblocks .NET Aspire integration for Blazor WebAssembly. Aspire reads configuration values such as `OTEL_EXPORTER_OTLP_ENDPOINT` from `IConfiguration` for service discovery and OpenTelemetry. For more information, see [dotnet/aspnetcore#64578](https://github.com/dotnet/aspnetcore/pull/64578). + +## Recommended action + +If you previously called `builder.Configuration.AddEnvironmentVariables()` manually, you can remove that call. The duplicate call is harmless but unnecessary. + +If an environment variable in your hosting environment shadows a value in `appsettings.json` and you don't want that, rename the environment variable, or add an additional configuration source after `CreateDefault` returns to override the value: + +```csharp +var builder = WebAssemblyHostBuilder.CreateDefault(args); + +// Re-apply appsettings.json on top of environment variables for a specific key. +builder.Configuration["Logging:LogLevel:Default"] = "Information"; +``` + +## Affected APIs + +- +- diff --git a/aspnetcore/toc.yml b/aspnetcore/toc.yml index 246244e001f5..53a781b2b122 100644 --- a/aspnetcore/toc.yml +++ b/aspnetcore/toc.yml @@ -11,6 +11,34 @@ items: items: - name: What's new in 11 uid: aspnetcore-11 + - name: Breaking changes + items: + - name: Overview + href: breaking-changes/11/overview.md + - name: Blazor custom event registration throws when name matches a browser event + href: breaking-changes/11/blazor-custom-event-name-collision.md + - name: Blazor enhanced navigation no longer preloads resources + href: breaking-changes/11/blazor-enhanced-nav-preloading-disabled.md + - name: ConcurrencyLimiter middleware removed + href: breaking-changes/11/concurrencylimiter-removed.md + - name: Hosting emits OpenTelemetry HTTP semantic-convention tags by default + href: breaking-changes/11/http-activity-otel-semconv.md + - name: Kestrel tightens HTTP protocol compliance + href: breaking-changes/11/kestrel-strict-protocol-compliance.md + - name: Microsoft.OpenApi upgraded to 3.x + href: breaking-changes/11/microsoft-openapi-3x.md + - name: Obsolete Blazor APIs removed + href: breaking-changes/11/blazor-obsolete-apis-removed.md + - name: OpenAPI server URL no longer has a trailing slash when PathBase is empty + href: breaking-changes/11/openapi-server-url-trailing-slash.md + - name: Passkey sign-in enforces email/phone confirmation and lockout + href: breaking-changes/11/passkey-signin-enforces-confirmation-lockout.md + - name: Response compression always emits Vary, Accept-Encoding + href: breaking-changes/11/response-compression-always-vary.md + - name: SqlClient Active Directory authentication moved to a separate package + href: breaking-changes/11/sqlclient-azure-extensions-required.md + - name: WebAssemblyHostBuilder loads environment variables into IConfiguration + href: breaking-changes/11/wasm-env-vars-in-configuration.md - name: ASP.NET Core 10 items: - name: What's new in 10 From 00bd52801c829c04cd96ab8dbbb2439e01dff6b5 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Thu, 4 Jun 2026 19:11:43 -0400 Subject: [PATCH 2/5] [WIP] [WIP] Address feedback on metadata placement in ASP.NET Core breaking changes documentation (#37229) * Initial plan * Move ai-usage metadata under title in breaking changes docs --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> --- .../breaking-changes/11/blazor-custom-event-name-collision.md | 2 +- .../11/blazor-enhanced-nav-preloading-disabled.md | 2 +- aspnetcore/breaking-changes/11/blazor-obsolete-apis-removed.md | 2 +- aspnetcore/breaking-changes/11/concurrencylimiter-removed.md | 2 +- aspnetcore/breaking-changes/11/http-activity-otel-semconv.md | 2 +- .../breaking-changes/11/kestrel-strict-protocol-compliance.md | 2 +- aspnetcore/breaking-changes/11/microsoft-openapi-3x.md | 2 +- .../breaking-changes/11/openapi-server-url-trailing-slash.md | 2 +- aspnetcore/breaking-changes/11/overview.md | 2 +- .../11/passkey-signin-enforces-confirmation-lockout.md | 2 +- .../breaking-changes/11/response-compression-always-vary.md | 2 +- .../breaking-changes/11/sqlclient-azure-extensions-required.md | 2 +- .../breaking-changes/11/wasm-env-vars-in-configuration.md | 2 +- 13 files changed, 13 insertions(+), 13 deletions(-) diff --git a/aspnetcore/breaking-changes/11/blazor-custom-event-name-collision.md b/aspnetcore/breaking-changes/11/blazor-custom-event-name-collision.md index a6d6bf4b2ed9..41bde8944cbe 100644 --- a/aspnetcore/breaking-changes/11/blazor-custom-event-name-collision.md +++ b/aspnetcore/breaking-changes/11/blazor-custom-event-name-collision.md @@ -1,8 +1,8 @@ --- title: "Breaking change: Blazor custom event registration throws when name matches a browser event" +ai-usage: ai-assisted description: "Learn about the breaking change in ASP.NET Core 11 where Blazor.registerCustomEventType throws when eventName equals browserEventName." ms.date: 06/04/2026 -ai-usage: ai-assisted --- # Blazor custom event registration throws when name matches a browser event diff --git a/aspnetcore/breaking-changes/11/blazor-enhanced-nav-preloading-disabled.md b/aspnetcore/breaking-changes/11/blazor-enhanced-nav-preloading-disabled.md index fdadcdc2cad9..3f6191f12653 100644 --- a/aspnetcore/breaking-changes/11/blazor-enhanced-nav-preloading-disabled.md +++ b/aspnetcore/breaking-changes/11/blazor-enhanced-nav-preloading-disabled.md @@ -1,8 +1,8 @@ --- title: "Breaking change: Blazor enhanced navigation no longer preloads resources" +ai-usage: ai-assisted description: "Learn about the breaking change in ASP.NET Core 11 where Blazor's ResourcePreloader no longer emits preload link hints for pages reached through enhanced navigation." ms.date: 06/04/2026 -ai-usage: ai-assisted --- # Blazor enhanced navigation no longer preloads resources diff --git a/aspnetcore/breaking-changes/11/blazor-obsolete-apis-removed.md b/aspnetcore/breaking-changes/11/blazor-obsolete-apis-removed.md index 32996c4f0dfd..4dd87a42c5d1 100644 --- a/aspnetcore/breaking-changes/11/blazor-obsolete-apis-removed.md +++ b/aspnetcore/breaking-changes/11/blazor-obsolete-apis-removed.md @@ -1,8 +1,8 @@ --- title: "Breaking change: Obsolete Blazor APIs removed" +ai-usage: ai-assisted description: "Learn about the breaking change in ASP.NET Core 11 where APIs that were marked obsolete in earlier Blazor releases have been removed." ms.date: 06/04/2026 -ai-usage: ai-assisted --- # Obsolete Blazor APIs removed diff --git a/aspnetcore/breaking-changes/11/concurrencylimiter-removed.md b/aspnetcore/breaking-changes/11/concurrencylimiter-removed.md index 9e08343b1961..62f5efc124f2 100644 --- a/aspnetcore/breaking-changes/11/concurrencylimiter-removed.md +++ b/aspnetcore/breaking-changes/11/concurrencylimiter-removed.md @@ -1,8 +1,8 @@ --- title: "Breaking change: ConcurrencyLimiter middleware removed" +ai-usage: ai-assisted description: "Learn about the breaking change in ASP.NET Core 11 where the Microsoft.AspNetCore.ConcurrencyLimiter package and middleware are removed. Use the rate-limiting middleware instead." ms.date: 06/04/2026 -ai-usage: ai-assisted --- # ConcurrencyLimiter middleware removed diff --git a/aspnetcore/breaking-changes/11/http-activity-otel-semconv.md b/aspnetcore/breaking-changes/11/http-activity-otel-semconv.md index 20c7099dad4d..bfdf2a03aded 100644 --- a/aspnetcore/breaking-changes/11/http-activity-otel-semconv.md +++ b/aspnetcore/breaking-changes/11/http-activity-otel-semconv.md @@ -1,8 +1,8 @@ --- title: "Breaking change: ASP.NET Core hosting emits OpenTelemetry HTTP semantic-convention tags by default" +ai-usage: ai-assisted description: "Learn about the breaking change in ASP.NET Core 11 where the HTTP server Activity emits OpenTelemetry HTTP semantic-convention tags by default, the http.route tag uses literal default values for conventional routes, and Activity.StatusDescription is no longer set on exceptions." ms.date: 06/04/2026 -ai-usage: ai-assisted --- # ASP.NET Core hosting emits OpenTelemetry HTTP semantic-convention tags by default diff --git a/aspnetcore/breaking-changes/11/kestrel-strict-protocol-compliance.md b/aspnetcore/breaking-changes/11/kestrel-strict-protocol-compliance.md index 94dc0ac9b747..ec976ff1480b 100644 --- a/aspnetcore/breaking-changes/11/kestrel-strict-protocol-compliance.md +++ b/aspnetcore/breaking-changes/11/kestrel-strict-protocol-compliance.md @@ -1,8 +1,8 @@ --- title: "Breaking change: Kestrel tightens HTTP protocol compliance" +ai-usage: ai-assisted description: "Learn about the breaking change in ASP.NET Core 11 where Kestrel rejects additional HTTP/2 and HTTP/3 connection-specific headers and closes HTTP/1.1 connections after a request with both Content-Length and Transfer-Encoding." ms.date: 06/04/2026 -ai-usage: ai-assisted --- # Kestrel tightens HTTP protocol compliance diff --git a/aspnetcore/breaking-changes/11/microsoft-openapi-3x.md b/aspnetcore/breaking-changes/11/microsoft-openapi-3x.md index 02a5db573f85..4090f74c7312 100644 --- a/aspnetcore/breaking-changes/11/microsoft-openapi-3x.md +++ b/aspnetcore/breaking-changes/11/microsoft-openapi-3x.md @@ -1,8 +1,8 @@ --- title: "Breaking change: Microsoft.OpenApi upgraded to 3.x" +ai-usage: ai-assisted description: "Learn about the breaking change in ASP.NET Core 11 where Microsoft.AspNetCore.OpenApi takes a dependency on Microsoft.OpenApi 3.x. Document and operation transformers might need updates." ms.date: 06/04/2026 -ai-usage: ai-assisted --- # Microsoft.OpenApi upgraded to 3.x diff --git a/aspnetcore/breaking-changes/11/openapi-server-url-trailing-slash.md b/aspnetcore/breaking-changes/11/openapi-server-url-trailing-slash.md index 36a5fe227dc7..08faa4f16af5 100644 --- a/aspnetcore/breaking-changes/11/openapi-server-url-trailing-slash.md +++ b/aspnetcore/breaking-changes/11/openapi-server-url-trailing-slash.md @@ -1,8 +1,8 @@ --- title: "Breaking change: OpenAPI server URL no longer has a trailing slash when PathBase is empty" +ai-usage: ai-assisted description: "Learn about the breaking change in ASP.NET Core 11 where the generated OpenAPI document's servers[0].url no longer ends with a trailing slash when the request PathBase is empty." ms.date: 06/04/2026 -ai-usage: ai-assisted --- # OpenAPI server URL no longer has a trailing slash when PathBase is empty diff --git a/aspnetcore/breaking-changes/11/overview.md b/aspnetcore/breaking-changes/11/overview.md index 274d3c2f2427..c43043fd36a8 100644 --- a/aspnetcore/breaking-changes/11/overview.md +++ b/aspnetcore/breaking-changes/11/overview.md @@ -1,9 +1,9 @@ --- title: Breaking changes in ASP.NET Core 11 +ai-usage: ai-assisted titleSuffix: "" description: Navigate to the breaking changes in ASP.NET Core 11. ms.date: 06/04/2026 -ai-usage: ai-assisted no-loc: [Blazor, Kestrel, SignalR] --- # Breaking changes in ASP.NET Core 11 diff --git a/aspnetcore/breaking-changes/11/passkey-signin-enforces-confirmation-lockout.md b/aspnetcore/breaking-changes/11/passkey-signin-enforces-confirmation-lockout.md index cc79cc175c69..dee38dbfc9b8 100644 --- a/aspnetcore/breaking-changes/11/passkey-signin-enforces-confirmation-lockout.md +++ b/aspnetcore/breaking-changes/11/passkey-signin-enforces-confirmation-lockout.md @@ -1,8 +1,8 @@ --- title: "Breaking change: Passkey sign-in enforces email/phone confirmation and lockout" +ai-usage: ai-assisted description: "Learn about the breaking change in ASP.NET Core 11 where PasskeySignInAsync calls PreSignInCheck so that confirmed-email, confirmed-phone, and lockout requirements are honored." ms.date: 06/04/2026 -ai-usage: ai-assisted --- # Passkey sign-in enforces email/phone confirmation and lockout diff --git a/aspnetcore/breaking-changes/11/response-compression-always-vary.md b/aspnetcore/breaking-changes/11/response-compression-always-vary.md index eaebe0533262..0d47a2190fb1 100644 --- a/aspnetcore/breaking-changes/11/response-compression-always-vary.md +++ b/aspnetcore/breaking-changes/11/response-compression-always-vary.md @@ -1,8 +1,8 @@ --- title: "Breaking change: Response compression always emits Vary: Accept-Encoding" +ai-usage: ai-assisted description: "Learn about the breaking change in ASP.NET Core 11 where the response compression middleware always appends Vary: Accept-Encoding to responses it sees, even when no compression was applied." ms.date: 06/04/2026 -ai-usage: ai-assisted --- # Response compression always emits Vary: Accept-Encoding diff --git a/aspnetcore/breaking-changes/11/sqlclient-azure-extensions-required.md b/aspnetcore/breaking-changes/11/sqlclient-azure-extensions-required.md index 37b6a3bb964f..34d5aad5ba17 100644 --- a/aspnetcore/breaking-changes/11/sqlclient-azure-extensions-required.md +++ b/aspnetcore/breaking-changes/11/sqlclient-azure-extensions-required.md @@ -1,8 +1,8 @@ --- title: "Breaking change: SqlClient Active Directory authentication moved to a separate package" +ai-usage: ai-assisted description: "Learn about the breaking change in ASP.NET Core 11 where Microsoft.Data.SqlClient 7.x extracts Active Directory authentication providers into the Microsoft.Data.SqlClient.Extensions.Azure package." ms.date: 06/04/2026 -ai-usage: ai-assisted --- # SqlClient Active Directory authentication moved to a separate package diff --git a/aspnetcore/breaking-changes/11/wasm-env-vars-in-configuration.md b/aspnetcore/breaking-changes/11/wasm-env-vars-in-configuration.md index 4debf8dd059e..bf73e9db156f 100644 --- a/aspnetcore/breaking-changes/11/wasm-env-vars-in-configuration.md +++ b/aspnetcore/breaking-changes/11/wasm-env-vars-in-configuration.md @@ -1,8 +1,8 @@ --- title: "Breaking change: WebAssemblyHostBuilder loads environment variables into IConfiguration" +ai-usage: ai-assisted description: "Learn about the breaking change in ASP.NET Core 11 where WebAssemblyHostBuilder.CreateDefault adds environment variables to IConfiguration by default." ms.date: 06/04/2026 -ai-usage: ai-assisted --- # WebAssemblyHostBuilder loads environment variables into IConfiguration From 9f274f3c970b673366c2a2c909f6c086914477c4 Mon Sep 17 00:00:00 2001 From: Luke Latham <1622880+guardrex@users.noreply.github.com> Date: Thu, 4 Jun 2026 19:20:18 -0400 Subject: [PATCH 3/5] Apply suggestions from code review Co-authored-by: Luke Latham <1622880+guardrex@users.noreply.github.com> --- .../11/blazor-custom-event-name-collision.md | 1 - .../11/blazor-enhanced-nav-preloading-disabled.md | 1 - .../11/blazor-obsolete-apis-removed.md | 5 ++--- .../breaking-changes/11/concurrencylimiter-removed.md | 1 - .../breaking-changes/11/http-activity-otel-semconv.md | 8 ++++---- .../11/kestrel-strict-protocol-compliance.md | 1 - .../breaking-changes/11/microsoft-openapi-3x.md | 11 ++++++----- .../11/openapi-server-url-trailing-slash.md | 3 +-- .../passkey-signin-enforces-confirmation-lockout.md | 3 +-- .../11/response-compression-always-vary.md | 5 ++--- .../11/sqlclient-azure-extensions-required.md | 4 ++-- .../11/wasm-env-vars-in-configuration.md | 9 ++++----- 12 files changed, 22 insertions(+), 30 deletions(-) diff --git a/aspnetcore/breaking-changes/11/blazor-custom-event-name-collision.md b/aspnetcore/breaking-changes/11/blazor-custom-event-name-collision.md index 41bde8944cbe..93fdd5ae72c8 100644 --- a/aspnetcore/breaking-changes/11/blazor-custom-event-name-collision.md +++ b/aspnetcore/breaking-changes/11/blazor-custom-event-name-collision.md @@ -4,7 +4,6 @@ ai-usage: ai-assisted description: "Learn about the breaking change in ASP.NET Core 11 where Blazor.registerCustomEventType throws when eventName equals browserEventName." ms.date: 06/04/2026 --- - # Blazor custom event registration throws when name matches a browser event The Blazor JavaScript API `Blazor.registerCustomEventType` now throws an error when the custom event name matches its `browserEventName` option. Registering a custom event with the same name as the underlying browser event caused the event to fire twice for each user action. diff --git a/aspnetcore/breaking-changes/11/blazor-enhanced-nav-preloading-disabled.md b/aspnetcore/breaking-changes/11/blazor-enhanced-nav-preloading-disabled.md index 3f6191f12653..1a8e09b9e57e 100644 --- a/aspnetcore/breaking-changes/11/blazor-enhanced-nav-preloading-disabled.md +++ b/aspnetcore/breaking-changes/11/blazor-enhanced-nav-preloading-disabled.md @@ -4,7 +4,6 @@ ai-usage: ai-assisted description: "Learn about the breaking change in ASP.NET Core 11 where Blazor's ResourcePreloader no longer emits preload link hints for pages reached through enhanced navigation." ms.date: 06/04/2026 --- - # Blazor enhanced navigation no longer preloads resources Blazor no longer emits `` or `` resource hints for pages reached through enhanced navigation. Resource preloading still works for the initial page load and for normal (non-enhanced) navigation. diff --git a/aspnetcore/breaking-changes/11/blazor-obsolete-apis-removed.md b/aspnetcore/breaking-changes/11/blazor-obsolete-apis-removed.md index 4dd87a42c5d1..db37bfdebc57 100644 --- a/aspnetcore/breaking-changes/11/blazor-obsolete-apis-removed.md +++ b/aspnetcore/breaking-changes/11/blazor-obsolete-apis-removed.md @@ -4,7 +4,6 @@ ai-usage: ai-assisted description: "Learn about the breaking change in ASP.NET Core 11 where APIs that were marked obsolete in earlier Blazor releases have been removed." ms.date: 06/04/2026 --- - # Obsolete Blazor APIs removed A set of APIs across the Blazor Components assemblies that were previously marked obsolete have been removed in ASP.NET Core 11. Most of these APIs were marked obsolete several releases ago and have working replacements that have been recommended for years. @@ -77,8 +76,8 @@ Update your code to use the replacement APIs as listed in the following table. ## Affected APIs - -- -- +- +- - `Microsoft.AspNetCore.Components.Forms.RemoteBrowserFileStreamOptions` - `Microsoft.AspNetCore.Components.Web.WebEventCallbackFactoryEventArgsExtensions` - `Microsoft.AspNetCore.Components.RenderTree.WebRenderer.RendererId` (init accessor) diff --git a/aspnetcore/breaking-changes/11/concurrencylimiter-removed.md b/aspnetcore/breaking-changes/11/concurrencylimiter-removed.md index 62f5efc124f2..fe079e969b71 100644 --- a/aspnetcore/breaking-changes/11/concurrencylimiter-removed.md +++ b/aspnetcore/breaking-changes/11/concurrencylimiter-removed.md @@ -4,7 +4,6 @@ ai-usage: ai-assisted description: "Learn about the breaking change in ASP.NET Core 11 where the Microsoft.AspNetCore.ConcurrencyLimiter package and middleware are removed. Use the rate-limiting middleware instead." ms.date: 06/04/2026 --- - # ConcurrencyLimiter middleware removed The `Microsoft.AspNetCore.ConcurrencyLimiter` package and its middleware have been removed from ASP.NET Core 11. The middleware was marked obsolete in ASP.NET Core 8 in favor of the rate-limiting middleware (`Microsoft.AspNetCore.RateLimiting`), which exposes the equivalent concurrency-limiting functionality through `System.Threading.RateLimiting`. diff --git a/aspnetcore/breaking-changes/11/http-activity-otel-semconv.md b/aspnetcore/breaking-changes/11/http-activity-otel-semconv.md index bfdf2a03aded..a70f0b7b472f 100644 --- a/aspnetcore/breaking-changes/11/http-activity-otel-semconv.md +++ b/aspnetcore/breaking-changes/11/http-activity-otel-semconv.md @@ -4,7 +4,6 @@ ai-usage: ai-assisted description: "Learn about the breaking change in ASP.NET Core 11 where the HTTP server Activity emits OpenTelemetry HTTP semantic-convention tags by default, the http.route tag uses literal default values for conventional routes, and Activity.StatusDescription is no longer set on exceptions." ms.date: 06/04/2026 --- - # ASP.NET Core hosting emits OpenTelemetry HTTP semantic-convention tags by default The HTTP server created by `Microsoft.AspNetCore.Hosting` now emits the OpenTelemetry HTTP server-span semantic-convention tags by default, and its `http.route` tag uses the resolved values of conventional-route default parameters. is no longer set when an unhandled exception occurs. Together, these changes bring the built-in `Microsoft.AspNetCore.Hosting` activity into line with the metadata produced by `OpenTelemetry.Instrumentation.AspNetCore`. @@ -42,7 +41,7 @@ For more information, see [dotnet/aspnetcore#64851](https://github.com/dotnet/as ## Recommended action -Most apps don't need to do anything — the new defaults match what `OpenTelemetry.Instrumentation.AspNetCore` already produced. +Most apps don't need to do anything—the new defaults match what `OpenTelemetry.Instrumentation.AspNetCore` already produced. If you observe **duplicate tags** because both the framework and `OpenTelemetry.Instrumentation.AspNetCore` add the same set, upgrade the instrumentation package to the latest version, which detects the built-in tags and skips adding them. @@ -51,13 +50,14 @@ If you have a custom span enricher or exporter that depended on the *absence* of To restore the pre-11 behavior (no OpenTelemetry semantic-convention tags from the framework), set the `AppContext` switch back to `true` early in app startup: ```csharp -AppContext.SetSwitch("Microsoft.AspNetCore.Hosting.SuppressActivityOpenTelemetryData", true); +AppContext.SetSwitch( + "Microsoft.AspNetCore.Hosting.SuppressActivityOpenTelemetryData", true); ``` The switch only controls the OpenTelemetry tags. The `http.route` value-substitution change and the `Activity.StatusDescription` change can't be reverted independently. ## Affected APIs -- +- `Microsoft.AspNetCore.Hosting.HostingHostBuilderExtensions` - - The OpenTelemetry [HTTP server span semantic conventions](https://opentelemetry.io/docs/specs/semconv/http/http-spans/#http-server-span). diff --git a/aspnetcore/breaking-changes/11/kestrel-strict-protocol-compliance.md b/aspnetcore/breaking-changes/11/kestrel-strict-protocol-compliance.md index ec976ff1480b..a1fffd82a2ae 100644 --- a/aspnetcore/breaking-changes/11/kestrel-strict-protocol-compliance.md +++ b/aspnetcore/breaking-changes/11/kestrel-strict-protocol-compliance.md @@ -4,7 +4,6 @@ ai-usage: ai-assisted description: "Learn about the breaking change in ASP.NET Core 11 where Kestrel rejects additional HTTP/2 and HTTP/3 connection-specific headers and closes HTTP/1.1 connections after a request with both Content-Length and Transfer-Encoding." ms.date: 06/04/2026 --- - # Kestrel tightens HTTP protocol compliance Kestrel in ASP.NET Core 11 makes two HTTP-protocol-compliance changes: diff --git a/aspnetcore/breaking-changes/11/microsoft-openapi-3x.md b/aspnetcore/breaking-changes/11/microsoft-openapi-3x.md index 4090f74c7312..45afcbbb66b7 100644 --- a/aspnetcore/breaking-changes/11/microsoft-openapi-3x.md +++ b/aspnetcore/breaking-changes/11/microsoft-openapi-3x.md @@ -4,7 +4,6 @@ ai-usage: ai-assisted description: "Learn about the breaking change in ASP.NET Core 11 where Microsoft.AspNetCore.OpenApi takes a dependency on Microsoft.OpenApi 3.x. Document and operation transformers might need updates." ms.date: 06/04/2026 --- - # Microsoft.OpenApi upgraded to 3.x `Microsoft.AspNetCore.OpenApi` in ASP.NET Core 11 takes a dependency on `Microsoft.OpenApi` 3.x (currently 3.6.0). The previous release (.NET 10) depended on `Microsoft.OpenApi` 2.x. This is a major-version bump of a transitive dependency, and several types that document and operation transformers receive have changed shape. @@ -15,7 +14,7 @@ ms.date: 06/04/2026 ## Previous behavior -Previously, `Microsoft.AspNetCore.OpenApi` depended on `Microsoft.OpenApi` 2.x. Implementations of the document, operation, and schema transformer interfaces worked against the 2.x object model — for example, against the `OpenApiSchema` shape that exposed nested schemas, references, extensions, and parsing helpers as 2.x concrete types: +Previously, `Microsoft.AspNetCore.OpenApi` depended on `Microsoft.OpenApi` 2.x. Implementations of the document, operation, and schema transformer interfaces worked against the 2.x object model—for example, against the `OpenApiSchema` shape that exposed nested schemas, references, extensions, and parsing helpers as 2.x concrete types: ```csharp using Microsoft.AspNetCore.OpenApi; @@ -26,8 +25,10 @@ builder.Services.AddOpenApi(options => { options.AddSchemaTransformer((schema, context, ct) => { - // Microsoft.OpenApi 2.x: OpenApiString was a concrete type in Microsoft.OpenApi.Any. - schema.Extensions["x-schema-id"] = new OpenApiString(context.JsonTypeInfo.Type.Name); + // Microsoft.OpenApi 2.x: OpenApiString was a concrete type + // in Microsoft.OpenApi.Any. + schema.Extensions["x-schema-id"] = + new OpenApiString(context.JsonTypeInfo.Type.Name); return Task.CompletedTask; }); }); @@ -42,7 +43,7 @@ Starting in ASP.NET Core 11, `Microsoft.AspNetCore.OpenApi` depends on `Microsof - The `ParseNode` parsing infrastructure was removed in favor of visitor- and writer-based APIs. - The `OpenApiReference` model was reshaped to better distinguish between local and external references. -Transformer code that does anything more than read-only inspection — sets extension values, walks nested schemas, or constructs new schemas — usually needs to be updated. +Transformer code that does anything more than read-only inspection—sets extension values, walks nested schemas, or constructs new schemas—usually needs to be updated. ## Type of breaking change diff --git a/aspnetcore/breaking-changes/11/openapi-server-url-trailing-slash.md b/aspnetcore/breaking-changes/11/openapi-server-url-trailing-slash.md index 08faa4f16af5..90b3c19f9625 100644 --- a/aspnetcore/breaking-changes/11/openapi-server-url-trailing-slash.md +++ b/aspnetcore/breaking-changes/11/openapi-server-url-trailing-slash.md @@ -4,7 +4,6 @@ ai-usage: ai-assisted description: "Learn about the breaking change in ASP.NET Core 11 where the generated OpenAPI document's servers[0].url no longer ends with a trailing slash when the request PathBase is empty." ms.date: 06/04/2026 --- - # OpenAPI server URL no longer has a trailing slash when PathBase is empty The server URL that ASP.NET Core writes into the generated OpenAPI document no longer ends with a trailing slash when the request is empty. Tooling or contract tests that string-compare `servers[0].url` need to be updated. @@ -72,5 +71,5 @@ builder.Services.AddOpenApi(options => ## Affected APIs -- +- `Microsoft.AspNetCore.OpenApi.OpenApiRouteHandlerBuilderExtensions` - Generated `servers[0].url` in the OpenAPI document produced by [`AddOpenApi`](/aspnet/core/fundamentals/openapi/aspnetcore-openapi). diff --git a/aspnetcore/breaking-changes/11/passkey-signin-enforces-confirmation-lockout.md b/aspnetcore/breaking-changes/11/passkey-signin-enforces-confirmation-lockout.md index dee38dbfc9b8..2593eff20b15 100644 --- a/aspnetcore/breaking-changes/11/passkey-signin-enforces-confirmation-lockout.md +++ b/aspnetcore/breaking-changes/11/passkey-signin-enforces-confirmation-lockout.md @@ -4,7 +4,6 @@ ai-usage: ai-assisted description: "Learn about the breaking change in ASP.NET Core 11 where PasskeySignInAsync calls PreSignInCheck so that confirmed-email, confirmed-phone, and lockout requirements are honored." ms.date: 06/04/2026 --- - # Passkey sign-in enforces email/phone confirmation and lockout `SignInManager.PasskeySignInAsync` now calls `PreSignInCheck` before signing the user in. This means a successful passkey assertion alone is no longer enough to sign in if the user's account is locked out or hasn't met the configured `RequireConfirmedEmail`, `RequireConfirmedPhoneNumber`, or `RequireConfirmedAccount` policies. This aligns passkey sign-in behavior with `PasswordSignInAsync` and the other sign-in methods. @@ -47,7 +46,7 @@ If your app currently relies on passkey sign-in to bypass these checks, the safe ## Affected APIs -- +- - - - diff --git a/aspnetcore/breaking-changes/11/response-compression-always-vary.md b/aspnetcore/breaking-changes/11/response-compression-always-vary.md index 0d47a2190fb1..47dabacc27aa 100644 --- a/aspnetcore/breaking-changes/11/response-compression-always-vary.md +++ b/aspnetcore/breaking-changes/11/response-compression-always-vary.md @@ -4,7 +4,6 @@ ai-usage: ai-assisted description: "Learn about the breaking change in ASP.NET Core 11 where the response compression middleware always appends Vary: Accept-Encoding to responses it sees, even when no compression was applied." ms.date: 06/04/2026 --- - # Response compression always emits Vary: Accept-Encoding The response compression middleware now appends `Vary: Accept-Encoding` to every response that passes through it, including responses for which it didn't actually compress the body. Downstream caches see more correct cache keys, at the cost of slightly more cache variants when caches don't normalize the `Accept-Encoding` request header. @@ -15,7 +14,7 @@ The response compression middleware now appends `Vary: Accept-Encoding` to every ## Previous behavior -Previously, the response compression middleware only appended `Vary: Accept-Encoding` to responses it actually compressed. Responses for which compression was skipped — for example, because the content type wasn't in `MimeTypes`, or because the client preferred `identity` encoding — were sent without the `Vary` header added by the middleware. +Previously, the response compression middleware only appended `Vary: Accept-Encoding` to responses it actually compressed. Responses for which compression was skipped—for example, because the content type wasn't in `MimeTypes`, or because the client preferred `identity` encoding—were sent without the `Vary` header added by the middleware. A downstream cache could therefore serve a compressed response that was stored for `Accept-Encoding: gzip` to a client that only accepted `identity` (and vice versa), unless the cache or the origin already added the header by another means. @@ -58,4 +57,4 @@ app.Use(async (context, next) => ## Affected APIs - -- +- diff --git a/aspnetcore/breaking-changes/11/sqlclient-azure-extensions-required.md b/aspnetcore/breaking-changes/11/sqlclient-azure-extensions-required.md index 34d5aad5ba17..327952313896 100644 --- a/aspnetcore/breaking-changes/11/sqlclient-azure-extensions-required.md +++ b/aspnetcore/breaking-changes/11/sqlclient-azure-extensions-required.md @@ -4,7 +4,6 @@ ai-usage: ai-assisted description: "Learn about the breaking change in ASP.NET Core 11 where Microsoft.Data.SqlClient 7.x extracts Active Directory authentication providers into the Microsoft.Data.SqlClient.Extensions.Azure package." ms.date: 06/04/2026 --- - # SqlClient Active Directory authentication moved to a separate package ASP.NET Core 11 transitively brings in `Microsoft.Data.SqlClient` 7.x (up from 5.x in .NET 10). Starting in `Microsoft.Data.SqlClient` 6.0, the Microsoft Entra ID (formerly Azure Active Directory) authentication providers are no longer in the main `Microsoft.Data.SqlClient` package; they ship in a separate [`Microsoft.Data.SqlClient.Extensions.Azure`](https://www.nuget.org/packages/Microsoft.Data.SqlClient.Extensions.Azure) package. Apps that use an Entra ID `Authentication=` value in their SQL connection string must add a reference to this package. @@ -42,7 +41,8 @@ If your app uses a SQL connection string with any `Active Directory *` `Authenti ```xml - + ``` diff --git a/aspnetcore/breaking-changes/11/wasm-env-vars-in-configuration.md b/aspnetcore/breaking-changes/11/wasm-env-vars-in-configuration.md index bf73e9db156f..a34bcdeb5e85 100644 --- a/aspnetcore/breaking-changes/11/wasm-env-vars-in-configuration.md +++ b/aspnetcore/breaking-changes/11/wasm-env-vars-in-configuration.md @@ -4,10 +4,9 @@ ai-usage: ai-assisted description: "Learn about the breaking change in ASP.NET Core 11 where WebAssemblyHostBuilder.CreateDefault adds environment variables to IConfiguration by default." ms.date: 06/04/2026 --- - # WebAssemblyHostBuilder loads environment variables into IConfiguration -`WebAssemblyHostBuilder.CreateDefault` now calls `AddEnvironmentVariables()` on the configuration builder. Environment variables that are passed into the Blazor WebAssembly runtime through `MonoConfig.environmentVariables` are now visible through `IConfiguration` in addition to . +`WebAssemblyHostBuilder.CreateDefault` now calls `AddEnvironmentVariables()` on the configuration builder. Environment variables that are passed into the Blazor WebAssembly runtime through `MonoConfig.environmentVariables` are now visible through `IConfiguration` in addition to . ## Version introduced @@ -15,7 +14,7 @@ ms.date: 06/04/2026 ## Previous behavior -Previously, environment variables that were available to a Blazor WebAssembly app through weren't loaded into `IConfiguration`. To read them through `IConfiguration`, you had to add the environment-variables provider manually: +Previously, environment variables that were available to a Blazor WebAssembly app through weren't loaded into `IConfiguration`. To read them through `IConfiguration`, you had to add the environment-variables provider manually: ```csharp var builder = WebAssemblyHostBuilder.CreateDefault(args); @@ -63,5 +62,5 @@ builder.Configuration["Logging:LogLevel:Default"] = "Information"; ## Affected APIs -- -- +- +- From 00de9b96e83c0211de9e61d32787379aff355030 Mon Sep 17 00:00:00 2001 From: Stephen Halter Date: Fri, 5 Jun 2026 10:31:52 -0700 Subject: [PATCH 4/5] Note cross-version/server scope of HTTP/1.1 framing fix Per BrennanConroy's nit on dotnet/AspNetCore.Docs#37228 (acknowledged by halter73), call out that the HTTP/1.1 connection-close behavior for requests with both Content-Length and Transfer-Encoding is also being shipped to earlier Kestrel versions and to IIS and HTTP.sys. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../breaking-changes/11/kestrel-strict-protocol-compliance.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/aspnetcore/breaking-changes/11/kestrel-strict-protocol-compliance.md b/aspnetcore/breaking-changes/11/kestrel-strict-protocol-compliance.md index a1fffd82a2ae..20d5cdf58b26 100644 --- a/aspnetcore/breaking-changes/11/kestrel-strict-protocol-compliance.md +++ b/aspnetcore/breaking-changes/11/kestrel-strict-protocol-compliance.md @@ -36,6 +36,9 @@ Both changes are stricter conformance to the HTTP specifications. Compliant clie **HTTP/1.1.** When a request includes both `Content-Length` and `Transfer-Encoding`, Kestrel still strips `Content-Length` and processes the request, but it now closes the connection after sending the response instead of keeping it alive. This mitigates a known HTTP request-smuggling vector. +> [!NOTE] +> The HTTP/1.1 connection-close behavior for requests that include both `Content-Length` and `Transfer-Encoding` is also being shipped in servicing updates to earlier supported versions of Kestrel and to IIS and HTTP.sys. + ## Type of breaking change These changes are [behavioral changes](/dotnet/core/compatibility/categories#behavioral-change). From f011a2761d36d2207f173031ffa39cbe39e3975a Mon Sep 17 00:00:00 2001 From: Stephen Halter Date: Fri, 5 Jun 2026 15:30:55 -0700 Subject: [PATCH 5/5] Document multiple ProducesResponseType per status code change Per @DeagleGross's review feedback on dotnet/AspNetCore.Docs#37228, adds a new breaking-change entry for dotnet/aspnetcore#65650, which preserves all [ProducesResponseType] / .Produces() declarations for a single status code instead of overwriting earlier ones. - Adds aspnetcore/breaking-changes/11/openapi-multiple-produces-per-status.md - Adds the entry to the .NET 11 overview table. - Adds the entry to the toc.yml ASP.NET Core 11 / Breaking changes block (alphabetical by display title). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../openapi-multiple-produces-per-status.md | 71 +++++++++++++++++++ aspnetcore/breaking-changes/11/overview.md | 1 + aspnetcore/toc.yml | 2 + 3 files changed, 74 insertions(+) create mode 100644 aspnetcore/breaking-changes/11/openapi-multiple-produces-per-status.md diff --git a/aspnetcore/breaking-changes/11/openapi-multiple-produces-per-status.md b/aspnetcore/breaking-changes/11/openapi-multiple-produces-per-status.md new file mode 100644 index 000000000000..ed6a87cf352f --- /dev/null +++ b/aspnetcore/breaking-changes/11/openapi-multiple-produces-per-status.md @@ -0,0 +1,71 @@ +--- +title: "Breaking change: OpenAPI document includes all ProducesResponseType entries per status code" +ai-usage: ai-assisted +description: "Learn about the breaking change in ASP.NET Core 11 where the generated OpenAPI document contains multiple response variants for a single status code instead of dropping all but the last declaration." +ms.date: 06/05/2026 +--- +# OpenAPI document includes all ProducesResponseType entries per status code + +ASP.NET Core 11 changes how MVC's `ApiExplorer` and minimal API's metadata-collection step handle multiple `[ProducesResponseType]` or declarations for the same status code. Previously, all but one declaration per status code were dropped before the OpenAPI document was generated. Starting in ASP.NET Core 11, all declarations are preserved, and the generated OpenAPI document reflects every declared content type and schema for each status code. + +## Version introduced + +.NET 11 + +## Previous behavior + +For both MVC controllers and minimal APIs, only one `ApiResponseType` survived per status code. Additional `[ProducesResponseType]` attributes or `.Produces(...)` calls with the same status code silently overwrote the previous entry. The generated OpenAPI document therefore contained a single response variant per status code, even when the developer declared several. + +For example, with the following minimal API endpoint: + +```csharp +app.MapGet("/items/{id}", (int id) => /* ... */) + .Produces(200) + .Produces(200, "text/xml"); +``` + +The generated OpenAPI document contained only the `Customer` variant for status `200`; the `Product` variant was silently dropped. + +The same overwrite behavior applied to controllers: + +```csharp +[ProducesResponseType(typeof(Foo), 200, "application/json")] +[ProducesResponseType(typeof(Bar), 200, "text/xml")] +public IActionResult Get() => /* ... */; +``` + +Only the `Bar` / `text/xml` variant survived in the OpenAPI document. + +## New behavior + +Starting in ASP.NET Core 11, all declared response types for the same status code are preserved and emitted to the generated OpenAPI document. For the minimal API example above, the `responses["200"]` entry now contains both the `application/json` schema (for `Product`) and the `text/xml` schema (for `Customer`). For the controller example, both the `application/json` schema (for `Foo`) and the `text/xml` schema (for `Bar`) are emitted. + +When multiple declarations share the same status code *and* the same content type but declare different types, the OpenAPI document represents the response schema as a union of the declared types. + +Controller-level `[Produces]` content types continue to apply as the shared default content type for entries that don't specify their own. Attribute-level declarations on an action take precedence over controller-level declarations with the same status code. + +## Type of breaking change + +This change is a [behavioral change](/dotnet/core/compatibility/categories#behavioral-change). + +## Reason for change + +Dropping all but one declared response variant produced incomplete OpenAPI documents and surprised users who expected each `[ProducesResponseType]` or `.Produces(...)` call to be reflected in the generated schema. The new behavior matches the developer's stated intent and is consistent with how OpenAPI represents multiple content types per status code. For more information, see [dotnet/aspnetcore#65650](https://github.com/dotnet/aspnetcore/pull/65650). + +## Recommended action + +Most apps benefit from the new behavior with no code changes. Verify that: + +- **Downstream OpenAPI consumers** (code generators such as NSwag, OpenAPI Generator, or Kiota; contract tests; client SDK builds) handle multiple response variants per status code. Most generators do, but the generated client code may now expose additional types or a union return type where it previously exposed a single type. +- **Snapshot tests against the generated OpenAPI document** are updated to expect the additional entries. +- **Duplicate or stale `[ProducesResponseType]` declarations** that were previously masked by the overwrite behavior are removed. Audit endpoints that have multiple declarations for the same status code and remove ones that no longer apply. + +If you intentionally want the old single-variant shape for a specific endpoint, remove the redundant declarations from the endpoint instead of relying on the overwrite behavior. + +## Affected APIs + +- +- +- +- +- Generated `responses` entries in the OpenAPI document produced by [`AddOpenApi`](/aspnet/core/fundamentals/openapi/aspnetcore-openapi). diff --git a/aspnetcore/breaking-changes/11/overview.md b/aspnetcore/breaking-changes/11/overview.md index c43043fd36a8..da30fc09b8cc 100644 --- a/aspnetcore/breaking-changes/11/overview.md +++ b/aspnetcore/breaking-changes/11/overview.md @@ -21,6 +21,7 @@ If you're migrating an app to ASP.NET Core 11, the breaking changes listed here | [Kestrel tightens HTTP protocol compliance](kestrel-strict-protocol-compliance.md) | Behavioral change | | [Microsoft.OpenApi upgraded to 3.x](microsoft-openapi-3x.md) | Source incompatible | | [Obsolete Blazor APIs removed](blazor-obsolete-apis-removed.md) | Source incompatible | +| [OpenAPI document includes all ProducesResponseType entries per status code](openapi-multiple-produces-per-status.md) | Behavioral change | | [OpenAPI server URL no longer has a trailing slash when PathBase is empty](openapi-server-url-trailing-slash.md) | Behavioral change | | [Passkey sign-in enforces email/phone confirmation and lockout](passkey-signin-enforces-confirmation-lockout.md) | Behavioral change | | [Response compression always emits Vary: Accept-Encoding](response-compression-always-vary.md) | Behavioral change | diff --git a/aspnetcore/toc.yml b/aspnetcore/toc.yml index 53a781b2b122..cbd109b326fe 100644 --- a/aspnetcore/toc.yml +++ b/aspnetcore/toc.yml @@ -29,6 +29,8 @@ items: href: breaking-changes/11/microsoft-openapi-3x.md - name: Obsolete Blazor APIs removed href: breaking-changes/11/blazor-obsolete-apis-removed.md + - name: OpenAPI document includes all ProducesResponseType entries per status code + href: breaking-changes/11/openapi-multiple-produces-per-status.md - name: OpenAPI server URL no longer has a trailing slash when PathBase is empty href: breaking-changes/11/openapi-server-url-trailing-slash.md - name: Passkey sign-in enforces email/phone confirmation and lockout