This browser is no longer supported.
Upgrade to Microsoft Edge to take advantage of the latest features, security updates, and technical support.
Note
Access to this page requires authorization. You can trysigning in orchanging directories.
Access to this page requires authorization. You can trychanging directories.
This article highlights the most significant changes in ASP.NET Core in .NET 8 with links to relevant documentation.
With the release of .NET 8, Blazor is a full-stack web UI framework for developing apps that render content at either the component or page level with:
Interactive render modes also prerender content by default.
For more information, see the following articles:
Examples throughout the Blazor documentation have been updated for use in Blazor Web Apps. Blazor Server examples remain in content versioned for .NET 7 or earlier.
We've added a new article that discusses component library authorship in Razor class libraries (RCLs) with static server-side rendering (static SSR).
For more information, seeASP.NET Core Razor class libraries (RCLs) with static server-side rendering (static SSR).
We've added a new article that discusses some of the common HTTP caching issues that can occur when upgrading Blazor apps across major versions and how to address HTTP caching issues.
For more information, seeAvoid HTTP caching issues when upgrading ASP.NET Core Blazor apps.
We've introduced a new Blazor project template: theBlazor Web App template. The new template provides a single starting point for using Blazor components to build any style of web UI. The template combines the strengths of the existing Blazor Server and Blazor WebAssembly hosting models with the new Blazor capabilities added in .NET 8: static server-side rendering (static SSR), streaming rendering, enhanced navigation and form handling, and the ability to add interactivity using either Blazor Server or Blazor WebAssembly on a per-component basis.
As part of unifying the various Blazor hosting models into a single model in .NET 8, we're also consolidating the number of Blazor project templates. We removed the Blazor Server template, and the ASP.NET Core Hosted option has been removed from the Blazor WebAssembly template. Both of these scenarios are represented by options when using the Blazor Web App template.
Note
Existing Blazor Server and Blazor WebAssembly apps remain supported in .NET 8. Optionally, these apps can be updated to use the new full-stack web UI Blazor features.
For more information on the new Blazor Web App template, see the following articles:
For Blazor Server, Blazor WebAssembly, and Blazor Hybrid apps:
beforeStart is used for tasks such as customizing the loading process, logging level, and other options.afterStarted is used for tasks such as registering Blazor event listeners and custom event types.The preceding legacy JS initializers aren't invoked by default in a Blazor Web App. For Blazor Web Apps, a new set of JS initializers are used:beforeWebStart,afterWebStarted,beforeServerStart,afterServerStarted,beforeWebAssemblyStart, andafterWebAssemblyStarted.
For more information, seeASP.NET Core Blazor startup.
For prior releases of .NET, we covered prerendering and integration in a single article. To simplify and focus our coverage, we've split the subjects into the following new articles, which have been updated for .NET 8:
You can persist and read component state in a Blazor Web App using the existingPersistentComponentState service. This is useful forpersisting component state during prerendering.
Blazor Web Apps automatically persist any registered app-level state created during prerendering, removing the need for thePersist Component State Tag Helper.
Blazor components can now handle submitted form requests, including model binding and validating the request data. Components can implement forms with separate form handlers using the standard HTML<form> tag or using the existingEditForm component.
Form model binding in Blazor honors the data contract attributes (for example,[DataMember] and[IgnoreDataMember]) for customizing how the form data is bound to the model.
New antiforgery support is included in .NET 8. A newAntiforgeryToken component renders an antiforgery token as a hidden field, and the new[RequireAntiforgeryToken] attribute enables antiforgery protection. If an antiforgery check fails, a 400 (Bad Request) response is returned without form processing. The new antiforgery features are enabled by default for forms based onEditform and can be applied manually to standard HTML forms.
For more information, seeASP.NET Core Blazor forms overview.
Static server-side rendering (static SSR) typically performs a full page refresh whenever the user navigates to a new page or submits a form. In .NET 8, Blazor can enhance page navigation and form handling by intercepting the request and performing a fetch request instead. Blazor then handles the rendered response content by patching it into the browser DOM. Enhanced navigation and form handling avoids the need for a full page refresh and preserves more of the page state, so pages load faster and more smoothly. Enhanced navigation is enabled by default when the Blazor script (blazor.web.js) is loaded. Enhanced form handling can be optionally enabled for specific forms.
New enhanced navigation API allows you to refresh the current page by callingNavigationManager.Refresh(bool forceLoad = false).
For more information, see the following sections of the BlazorRouting article:
Some apps depend on JS interop to perform initialization tasks that are specific to each page. When using Blazor's enhanced navigation feature with statically-rendered pages that perform JS interop initialization tasks, page-specific JS may not be executed again as expected each time an enhanced page navigation occurs. A new article explains how to address this scenario in Blazor Web Apps:
ASP.NET Core Blazor JavaScript with static server-side rendering (static SSR)
You can now stream content updates on the response stream when using static server-side rendering (static SSR) with Blazor. Streaming rendering can improve the user experience for pages that perform long-running asynchronous tasks in order to fully render by rendering content as soon as it's available.
For example, to render a page you might need to make a long running database query or an API call. Normally, asynchronous tasks executed as part of rendering a page must complete before the rendered response is sent, which can delay loading the page. Streaming rendering initially renders the entire page with placeholder content while asynchronous operations execute. After the asynchronous operations are complete, the updated content is sent to the client on the same response connection and patched by into the DOM. The benefit of this approach is that the main layout of the app renders as quickly as possible and the page is updated as soon as the content is ready.
For more information, seeASP.NET Core Razor component rendering.
Blazor now supports injecting keyed services using the[Inject] attribute. Keys allow for scoping of registration and consumption of services when using dependency injection. Use the newInjectAttribute.Key property to specify the key for the service to inject:
[Inject(Key = "my-service")]public IMyService MyService { get; set; }The@inject Razor directive doesn't support keyed services for this release, but work is tracked byUpdate@inject to support keyed services (dotnet/razor #9286) for a future .NET release.
For more information, seeASP.NET Core Blazor dependency injection.
HttpContext as a cascading parameterYou can now access the currentHttpContext as a cascading parameter from a static server component:
[CascadingParameter]public HttpContext? HttpContext { get; set; }Accessing theHttpContext from a static server component might be useful for inspecting and modifying headers or other properties.
For an example that passesHttpContext state, access and refresh tokens, to components, seeASP.NET Core server-side and Blazor Web App additional security scenarios.
You can now render Razor components outside the context of an HTTP request. You can render Razor components as HTML directly to a string or stream independently of the ASP.NET Core hosting environment. This is convenient for scenarios where you want to generate HTML fragments, such as for a generating email or static site content.
For more information, seeRender Razor components outside of ASP.NET Core.
The newSectionOutlet andSectionContent components in Blazor add support for specifying outlets for content that can be filled in later. Sections are often used to define placeholders in layouts that are then filled in by specific pages. Sections are referenced either by a unique name or using a unique object ID.
For more information, seeASP.NET Core Blazor sections.
Blazor Web Apps can define a custom error page for use with theASP.NET Core exception handling middleware. The Blazor Web App project template includes a default error page with similar content to the one used in MVC and Razor Pages apps. When the error page is rendered in response to a request from Exception Handling Middleware, the error page always renders as a static server component, even if interactivity is otherwise enabled.
See theError component (Components/Pages/Error.razor) of the server project in theBlazor Web App project template (dotnet/aspnetcore GitHub repository).
Note
Documentation links to .NET reference source usually load the repository's default branch, which represents the current development for the next release of .NET. To select a tag for a specific release, use theSwitch branches or tags dropdown list. For more information, seeHow to select a version tag of ASP.NET Core source code (dotnet/AspNetCore.Docs #26205).
The Blazor QuickGrid component is no longer experimental and is now part of the Blazor framework in .NET 8.
QuickGrid is a high performance grid component for displaying data in tabular form. QuickGrid is built to be a simple and convenient way to display your data, while still providing powerful features, such as sorting, filtering, paging, and virtualization.
For more information, seeASP.NET Core Blazor `QuickGrid` component.
Blazor now supports using client-side routing to navigate to a specific HTML element on a page using standard URL fragments. If you specify an identifier for an HTML element using the standardid attribute, Blazor correctly scrolls to that element when the URL fragment matches the element identifier.
For more information, seeASP.NET Core Blazor routing and navigation.
Root-level cascading values can be registered for the entire component hierarchy. Named cascading values and subscriptions for update notifications are supported.
For more information, see the following resources:
Use the newEmptyContent parameter on theVirtualize component to supply content when the component has loaded and eitherItems is empty orItemsProviderResult<T>.TotalItemCount is zero.
For more information, seeASP.NET Core Razor component virtualization.
Interactive server components handle web UI events using a real-time connection with the browser called a circuit. A circuit and its associated state are set up when a root interactive server component is rendered. The circuit is closed when there are no remaining interactive server components on the page, which frees up server resources.
You can now monitor inbound circuit activity in server-side apps using the newCreateInboundActivityHandler method onCircuitHandler. Inbound circuit activity is any activity sent from the browser to the server, such as UI events or JavaScript-to-.NET interop calls.
For more information, seeASP.NET Core Blazor SignalR guidance.
TheJiterpreter is a new runtime feature in .NET 8 that enables partial Just-in-Time (JIT) compilation support when running on WebAssembly to achieve improved runtime performance.
For more information, seeASP.NET Core Blazor WebAssembly build tools and ahead-of-time (AOT) compilation.
Blazor WebAssembly ahead-of-time (AOT) compilation now usesWebAssembly Fixed-width SIMD andWebAssembly Exception handling by default to improve runtime performance.
For more information, see the following articles:
Webcil is web-friendly packaging of .NET assemblies that removes content specific to native Windows execution to avoid issues when deploying to environments that block the download or use of.dll files. Webcil is enabled by default for Blazor WebAssembly apps.
For more information, seeHost and deploy ASP.NET Core Blazor WebAssembly.
Note
Prior to the release of .NET 8, guidance inDeployment layout for ASP.NET Core hosted Blazor WebAssembly apps addresses environments that block clients from downloading and executing DLLs with a multipart bundling approach. In .NET 8 or later, Blazor uses the Webcil file format to address this problem. Multipart bundling using the experimental NuGet package described by theWebAssembly deployment layout article isn't supported for Blazor apps in .NET 8 or later. For more information, seeEnhanceMicrosoft.AspNetCore.Components.WebAssembly.MultipartBundle package to define a custom bundle format (dotnet/aspnetcore #36978). If you desire to continue using the multipart bundle package in .NET 8 or later apps, you can use the guidance in the article to create your own multipart bundling NuGet package, but it won't be supported by Microsoft.
When debugging .NET on WebAssembly, the debugger now downloads symbol data from symbol locations that are configured in Visual Studio preferences. This improves the debugging experience for apps that use NuGet packages.
You can now debug Blazor WebAssembly apps using Firefox. Debugging Blazor WebAssembly apps requires configuring the browser for remote debugging and then connecting to the browser using the browser developer tools through the .NET WebAssembly debugging proxy. Debugging Firefox from Visual Studio isn't supported at this time.
For more information, seeDebug ASP.NET Core Blazor apps.
Blazor WebAssembly no longer requires enabling theunsafe-eval script source when specifying a Content Security Policy (CSP).
For more information, seeEnforce a Content Security Policy for ASP.NET Core Blazor.
UseComponentBase.DispatchExceptionAsync in a Razor component to process exceptions thrown outside of the component's lifecycle call stack. This permits the component's code to treat exceptions as though they're lifecycle method exceptions. Thereafter, Blazor's error handling mechanisms, such as error boundaries, can process exceptions.
For more information, seeHandle errors in ASP.NET Core Blazor apps.
The .NET WebAssembly runtime can now be configured for Blazor startup.
For more information, seeASP.NET Core Blazor startup.
HubConnectionBuilderPrior workarounds for configuring hub connection timeouts can be replaced with formal SignalR hub connection builder timeout configuration.
For more information, see the following:
The Blazor project templates no longer depend onOpen Iconic for icons.
Blazor now supports thecancel andclose events on thedialog HTML element.
In the following example:
OnClose is called when themy-dialog dialog is closed with theClose button.OnCancel is called when the dialog is canceled with theEsc key. When an HTML dialog is dismissed with theEsc key, both thecancel andclose events are triggered.<div> <p>Output: @message</p> <button> Show modal dialog </button> <dialog @onclose="OnClose" @oncancel="OnCancel"> <p>Hi there!</p> <form method="dialog"> <button>Close</button> </form> </dialog></div>@code { private string? message; private void OnClose(EventArgs e) => message += "onclose, "; private void OnCancel(EventArgs e) => message += "oncancel, ";}Blazor supports generating a full Blazor-based Identity UI when you choose the authentication option forIndividual Accounts. You can either select the option for Individual Accounts in the new project dialog for Blazor Web Apps from Visual Studio or pass the-au|--auth option set toIndividual from the command line when you create a new project.
For more information, see the following resources:
The Blazor documentation hosts a new article and sample app to cover securing a standalone Blazor WebAssembly app with ASP.NET Core Identity.
For more information, see the following resources:
Routing and deep linking for Blazor Server with Yarp work correctly in .NET 8.
For more information, seeMigrate from ASP.NET Core in .NET 7 to .NET 8.
Support for multiple Blazor Web Apps per server project is under consideration for a future .NET release.
For more information, seeSupport for multiple Blazor Web apps per server project (dotnet/aspnetcore #52216).
The following articles document changes for Blazor Hybrid in .NET 8:
BlazorWebView gains aTryDispatchAsync method that calls a specifiedAction<ServiceProvider> asynchronously and passes in the scoped services available in Razor components. This enables code from the native UI to access scoped services such asNavigationManager.BlazorWebView.StartPath property to get or set the path for initial navigation within the Blazor navigation context when the Razor component is finished loading.[Parameter] attribute is no longer required when supplied from the query stringThe[Parameter] attribute is no longer required when supplying a parameter from the query string:
- [Parameter] [SupplyParameterFromQuery]ServerTimeout (default: 30 seconds) andKeepAliveInterval (default: 15 seconds) can be set directly onHubConnectionBuilder.
The following example shows the assignment of values that are double the default values in .NET 7 or earlier:
var connection = new signalR.HubConnectionBuilder() .withUrl("/chatHub") .build();connection.serverTimeoutInMilliseconds = 60000;connection.keepAliveIntervalInMilliseconds = 30000;The following example shows thenew approach for assigning values that are double the default values in .NET 8 or later:
var connection = new signalR.HubConnectionBuilder() .withUrl("/chatHub") .withServerTimeout(60000) .withKeepAlive(30000) .build();The following example shows the assignment of values that are double the default values in .NET 7 or earlier:
Blazor.start({ configureSignalR: function (builder) { let c = builder.build(); c.serverTimeoutInMilliseconds = 60000; c.keepAliveIntervalInMilliseconds = 30000; builder.build = () => { return c; }; }});The following example shows thenew approach for assigning values that are double the default values in .NET 8 or later for Blazor Web Apps and Blazor Server.
Blazor Web App:
Blazor.start({ circuit: { configureSignalR: function (builder) { builder.withServerTimeout(60000).withKeepAliveInterval(30000); } }});Blazor Server:
Blazor.start({ configureSignalR: function (builder) { builder.withServerTimeout(60000).withKeepAliveInterval(30000); }});The following example shows the assignment of values that are double the default values in .NET 7 or earlier:
var builder = new HubConnectionBuilder() .WithUrl(Navigation.ToAbsoluteUri("/chathub")) .Build();builder.ServerTimeout = TimeSpan.FromSeconds(60);builder.KeepAliveInterval = TimeSpan.FromSeconds(30);builder.On<string, string>("ReceiveMessage", (user, message) => ...await builder.StartAsync();The following example shows thenew approach for assigning values that are double the default values in .NET 8 or later:
var builder = new HubConnectionBuilder() .WithUrl(Navigation.ToAbsoluteUri("/chathub")) .WithServerTimeout(TimeSpan.FromSeconds(60)) .WithKeepAliveInterval(TimeSpan.FromSeconds(30)) .Build();builder.On<string, string>("ReceiveMessage", (user, message) => ...await builder.StartAsync();SignalR stateful reconnect reduces the perceived downtime of clients that have a temporary disconnect in their network connection, such as when switching network connections or a short temporary loss in access.
Stateful reconnect achieves this by:
Stateful reconnect is available in .NET 8 or later.
Opt in to stateful reconnect at both the server hub endpoint and the client:
Update the server hub endpoint configuration to enable theAllowStatefulReconnects option:
app.MapHub<MyHub>("/hubName", options =>{ options.AllowStatefulReconnects = true;});Optionally, the maximum buffer size in bytes allowed by the server can be set globally or for a specific hub with theStatefulReconnectBufferSize option:
TheStatefulReconnectBufferSize option set globally:
builder.AddSignalR(o => o.StatefulReconnectBufferSize = 1000);TheStatefulReconnectBufferSize option set for a specific hub:
builder.AddSignalR().AddHubOptions<MyHub>(o => o.StatefulReconnectBufferSize = 1000);TheStatefulReconnectBufferSize option is optional with a default of 100,000 bytes.
Update JavaScript or TypeScript client code to enable thewithStatefulReconnect option:
const builder = new signalR.HubConnectionBuilder() .withUrl("/hubname") .withStatefulReconnect({ bufferSize: 1000 }); // Optional, defaults to 100,000const connection = builder.build();ThebufferSize option is optional with a default of 100,000 bytes.
Update .NET client code to enable theWithStatefulReconnect option:
var builder = new HubConnectionBuilder() .WithUrl("<hub url>") .WithStatefulReconnect(); builder.Services.Configure<HubConnectionOptions>(o => o.StatefulReconnectBufferSize = 1000); var hubConnection = builder.Build();TheStatefulReconnectBufferSize option is optional with a default of 100,000 bytes.
For more information, seeConfigure stateful reconnect.
This section describes new features for minimal APIs. See alsothe section on Native AOT for more information relevant to minimal APIs.
Starting in .NET 8, theRequestLocalizationOptions.CultureInfoUseUserOverride property allows the application to decide whether or not to use nondefault Windows settings for theCultureInfoDateTimeFormat andNumberFormat properties. This has no impact on Linux. This directly corresponds toUseUserOverride.
app.UseRequestLocalization(options => { options.CultureInfoUseUserOverride = false; });Explicit binding to form values using the[FromForm] attribute is now supported. Parameters bound to the request with[FromForm] include anantiforgery token. The antiforgery token is validated when the request is processed.
Inferred binding to forms using theIFormCollection,IFormFile, andIFormFileCollection types is also supported.OpenAPI metadata is inferred for form parameters to support integration withSwagger UI.
For more information, see:
Binding from forms is now supported for:
Todo orProjectFor more information, seeBind to collections and complex types from forms.
This release adds a middleware for validating antiforgery tokens, which are used to mitigate cross-site request forgery attacks. CallAddAntiforgery to register antiforgery services in DI.WebApplicationBuilder automatically adds the middleware when the antiforgery services have been registered in the DI container. Antiforgery tokens are used to mitigatecross-site request forgery attacks.
var builder = WebApplication.CreateBuilder();builder.Services.AddAntiforgery();var app = builder.Build();app.UseAntiforgery();app.MapGet("/", () => "Hello World!");app.Run();The antiforgery middleware:
The antiforgery token is only validated if:
RequiresValidation=true.For more information, seeAntiforgery with Minimal APIs.
IResettable interface inObjectPoolMicrosoft.Extensions.ObjectPool provides support for pooling object instances in memory. Apps can use an object pool if the values are expensive to allocate or initialize.
In this release, we've made the object pool easier to use by adding theIResettable interface. Reusable types often need to be reset back to a default state between uses.IResettable types are automatically reset when returned to an object pool.
For more information, see theObjectPool sample.
Support for.NET native ahead-of-time (AOT) has been added. Apps that are published using AOT can have substantially better performance: smaller app size, less memory usage, and faster startup time. Native AOT is currently supported by gRPC, minimal API, and worker service apps. For more information, seeASP.NET Core support for Native AOT andTutorial: Publish an ASP.NET Core app using Native AOT. For information about known issues with ASP.NET Core and Native AOT compatibility, see GitHub issuedotnet/core #8288.
Many of the popular libraries used in ASP.NET Core projects currently have some compatibility issues when used in a project targeting Native AOT, such as:
Libraries using these dynamic features need to be updated in order to work with Native AOT. They can be updated using tools like Roslyn source generators.
Library authors hoping to support Native AOT are encouraged to:
The newASP.NET Core Web API (Native AOT) project template (short namewebapiaot) creates a project with AOT publish enabled. For more information, seeThe Web API (Native AOT) template.
CreateSlimBuilder methodTheCreateSlimBuilder() method used in the Web API (Native AOT) template initializes theWebApplicationBuilder with the minimum ASP.NET Core features necessary to run an app. TheCreateSlimBuilder method includes the following features that are typically needed for an efficient development experience:
appsettings.json andappsettings.{EnvironmentName}.json.For more information, seeTheCreateSlimBuilder method.
CreateEmptyBuilder methodThere's another newWebApplicationBuilder factory method for building small apps that only contain necessary features:WebApplication.CreateEmptyBuilder(WebApplicationOptions options). ThisWebApplicationBuilder is created with no built-in behavior. The app it builds contains only the services and middleware that are explicitly configured.
Here’s an example of using this API to create a small web application:
var builder = WebApplication.CreateEmptyBuilder(new WebApplicationOptions());builder.WebHost.UseKestrelCore();var app = builder.Build();app.Use(async (context, next) =>{ await context.Response.WriteAsync("Hello, World!"); await next(context);});Console.WriteLine("Running...");app.Run();Publishing this code with Native AOT using .NET 8 Preview 7 on a linux-x64 machine results in a self-contained native executable of about 8.5 MB.
We've further reduced Native AOT binary size for apps that don't need HTTPS or HTTP/3 support. Not using HTTPS or HTTP/3 is common for apps that run behind a TLS termination proxy (for example, hosted on Azure). The newWebApplication.CreateSlimBuilder method omits this functionality by default. It can be added by callingbuilder.WebHost.UseKestrelHttpsConfiguration() for HTTPS orbuilder.WebHost.UseQuic() for HTTP/3. For more information, seeTheCreateSlimBuilder method.
IAsyncEnumerable<T> typesNew features were added toSystem.Text.Json to better support Native AOT. These new features add capabilities for the source generation mode ofSystem.Text.Json, because reflection isn't supported by AOT.
One of the new features is support for JSON serialization ofIAsyncEnumerable<T> implementations implemented by the C# compiler. This support opens up their use in ASP.NET Core projects configured to publish Native AOT.
This API is useful in scenarios where a route handler usesyield return to asynchronously return an enumeration. For example, to materialize rows from a database query. For more information, seeUnspeakable type support in the .NET 8 Preview 4 announcement.
For information abut other improvements inSystem.Text.Json source generation, seeSerialization improvements in .NET 8.
The main entry points to subsystems that don't work reliably with Native AOT are now annotated. When these methods are called from an application with Native AOT enabled, a warning is provided. For example, the following code produces a warning at the invocation ofAddControllers because this API isn't trim-safe and isn't supported by Native AOT.

In order to make Minimal APIs compatible with Native AOT, we're introducing the Request Delegate Generator (RDG). The RDG is a source generator that does what theRequestDelegateFactory (RDF) does. That is, it turns the variousMapGet(),MapPost(), and calls like them intoRequestDelegate instances associated with the specified routes. But rather than doing it in-memory in an application when it starts, the RDG does it at compile time and generates C# code directly into the project. The RDG:
We're working to ensure that as many as possible of the Minimal API features are supported by the RDG and thus compatible with Native AOT.
The RDG is enabled automatically in a project when publishing with Native AOT is enabled. RDG can be manually enabled even when not using Native AOT by setting<EnableRequestDelegateGenerator>true</EnableRequestDelegateGenerator> in the project file. This can be useful when initially evaluating a project's readiness for Native AOT, or to reduce the startup time of an app.
The Request Delegate Generator uses the newC# 12 interceptors compiler feature to support intercepting calls to minimal APIMap methods with statically generated variants at runtime. The use of interceptors results in increased startup performance for apps compiled withPublishAot.
Minimal APIs generated at run time support automatically logging (or throwing exceptions in Development environments) when parameter binding fails. .NET 8 introduces the same support for APIs generated at compile time via theRequest Delegate Generator (RDG). For more information, seeLogging and exception handling in compile-time generated minimal APIs.
Minimal APIs are optimized for receiving and returning JSON payloads usingSystem.Text.Json, so the compatibility requirements for JSON and Native AOT apply too. Native AOT compatibility requires the use of theSystem.Text.Json source generator. All types accepted as parameters to or returned from request delegates in Minimal APIs must be configured on aJsonSerializerContext that is registered via ASP.NET Core's dependency injection, for example:
// Register the JSON serializer context with DIbuilder.Services.ConfigureHttpJsonOptions(options =>{ options.SerializerOptions.TypeInfoResolverChain.Insert(0, AppJsonSerializerContext.Default);});...// Add types used in the minimal API app to source generated JSON serializer content[JsonSerializable(typeof(Todo[]))]internal partial class AppJsonSerializerContext : JsonSerializerContext{}For more information about theTypeInfoResolverChain API, see the following resources:
Many of the common libraries available for ASP.NET Core projects today have some compatibility issues if used in a project targeting Native AOT. Popular libraries often rely on the dynamic capabilities of .NET reflection to inspect and discover types, conditionally load libraries at runtime, and generate code on the fly to implement their functionality. These libraries need to be updated in order to work with Native AOT by using tools likeRoslyn source generators.
Library authors wishing to learn more about preparing their libraries for Native AOT are encouraged to start bypreparing their library for trimming and learning more about theNative AOT compatibility requirements.
There are several new features for Kestrel and HTTP.sys.
Named pipes is a popular technology for building inter-process communication (IPC) between Windows apps. You can now build an IPC server using .NET, Kestrel, and named pipes.
var builder = WebApplication.CreateBuilder(args);builder.WebHost.ConfigureKestrel(serverOptions =>{ serverOptions.ListenNamedPipe("MyPipeName");});For more information about this feature and how to use .NET and gRPC to create an IPC server and client, seeInter-process communication with gRPC.
We’ve improved named pipe connection performance. Kestrel’s named pipe transport now accepts connections in parallel, and reusesNamedPipeServerStream instances.
Time to create 100,000 connections:
.NET 8 adds support for Application-Layer Protocol Negotiation (ALPN) to macOS. ALPN is a TLS feature used to negotiate which HTTP protocol a connection will use. For example, ALPN allows browsers and other HTTP clients to request an HTTP/2 connection. This feature is especially useful for gRPC apps, which require HTTP/2. For more information, seeUse HTTP/2 with the ASP.NET Core Kestrel web server.
TLS certificatesconfigured by path are now monitored for changes whenreloadOnChange is passed toKestrelServerOptions.Configure(). A change to the certificate file is treated the same way as a change to the configured path (that is, endpoints are reloaded).
Note that file deletions are specifically not tracked since they arise transiently and would crash the server if non-transient.
If TLS is disabled and HTTP/1.x is available, HTTP/2 and HTTP/3 will be disabled, even if they've been specified. This can cause some nasty surprises, so we've added warning output to let you know when it happens.
HTTP_PORTS andHTTPS_PORTS config keysApplications and containers are often only given a port to listen on, like 80, without additional constraints like host or path.HTTP_PORTS andHTTPS_PORTS are new config keys that allow specifying the listening ports for the Kestrel and HTTP.sys servers. These can be defined with theDOTNET_ orASPNETCORE_ environment variable prefixes, or specified directly through any other config input like appsettings.json. Each is a semicolon delimited list of port values. For example:
ASPNETCORE_HTTP_PORTS=80;8080ASPNETCORE_HTTPS_PORTS=443;8081This is shorthand for the following, which specifies the scheme (HTTP or HTTPS) and any host or IP:
ASPNETCORE_URLS=http://*:80/;http://*:8080/;https://*:443/;https://*:8081/For more information, seeConfigure endpoints for the ASP.NET Core Kestrel web server andHTTP.sys web server implementation in ASP.NET Core.
The Server Name Indication (SNI) host name is now exposed in theHostName property of theITlsHandshakeFeature interface.
SNI is part of theTLS handshake process. It allows clients to specify the host name they're attempting to connect to when the server hosts multiple virtual hosts or domains. To present the correct security certificate during the handshake process, the server needs to know the host name selected for each request.
Normally the host name is only handled within the TLS stack and is used to select the matching certificate. But by exposing it, other components in an app can use that information for purposes such as diagnostics, rate limiting, routing, and billing.
Exposing the host name is useful for large-scale services managing thousands of SNI bindings. This feature can significantly improve debugging efficiency during customer escalations. The increased transparency allows for faster problem resolution and enhanced service reliability.
For more information, seeITlsHandshakeFeature.HostName.
using Microsoft.AspNetCore.Http.Features;using Microsoft.AspNetCore.Server.HttpSys;var builder = WebApplication.CreateBuilder(args);builder.WebHost.UseHttpSys();var app = builder.Build();app.Use((context, next) =>{ var feature = context.Features.GetRequiredFeature<IHttpSysRequestTimingFeature>(); var loggerFactory = context.RequestServices.GetRequiredService<ILoggerFactory>(); var logger = loggerFactory.CreateLogger("Sample"); var timingType = HttpSysRequestTimingType.RequestRoutingEnd; if (feature.TryGetTimestamp(timingType, out var timestamp)) { logger.LogInformation("Timestamp {timingType}: {timestamp}", timingType, timestamp); } else { logger.LogInformation("Timestamp {timingType}: not available for the " + "current request", timingType); } return next(context);});app.MapGet("/", () => Results.Ok());app.Run();For more information, seeGet detailed timing information with IHttpSysRequestTimingFeature andTiming information and In-process hosting with IIS.
In some scenarios, high volumes of small writes with high latency can cause significant performance impact toHTTP.sys. This impact is due to the lack of aPipe buffer in theHTTP.sys implementation. To improve performance in these scenarios, support for response buffering has been added toHTTP.sys. Enable buffering by settingHttpSysOptions.EnableKernelResponseBuffering totrue.
Response buffering should be enabled by an app that does synchronous I/O, or asynchronous I/O with no more than one outstanding write at a time. In these scenarios, response buffering can significantly improve throughput over high-latency connections.
Apps that use asynchronous I/O and that can have more than one write outstanding at a time shouldnot use this flag. Enabling this flag can result in higher CPU and memory usage by HTTP.Sys.
.NET 8 adds new features to authentication and authorization.
MapIdentityApi<TUser> is a new extension method that adds two API endpoints (/register and/login). The main goal of theMapIdentityApi is to make it easy for developers to use ASP.NET Core Identity for authentication in JavaScript-based single page apps (SPA) or Blazor apps. Instead of using the default UI provided by ASP.NET Core Identity, which is based on Razor Pages, MapIdentityApi adds JSON API endpoints that are more suitable for SPA apps and nonbrowser apps. For more information, seeIdentity API endpoints.
IAuthorizationRequirementDataPrior to the release of .NET 8, adding a parameterized authorization policy to an endpoint required implementing:
AuthorizeAttribute for each policy.AuthorizationPolicyProvider to process a custom policy from a string-based contract.AuthorizationRequirement for the policy.AuthorizationHandler for each requirement.For example, consider the following code written for .NET 7:
using AuthRequirementsData.Authorization;using Microsoft.AspNetCore.Authorization;var builder = WebApplication.CreateBuilder();builder.Services.AddAuthentication().AddJwtBearer();builder.Services.AddAuthorization();builder.Services.AddControllers();builder.Services.AddSingleton<IAuthorizationPolicyProvider, MinimumAgePolicyProvider>();builder.Services.AddSingleton<IAuthorizationHandler, MinimumAgeAuthorizationHandler>();var app = builder.Build();app.MapControllers();app.Run();using Microsoft.AspNetCore.Mvc;namespace AuthRequirementsData.Controllers;[ApiController][Route("api/[controller]")]public class GreetingsController : Controller{ [MinimumAgeAuthorize(16)] [HttpGet("hello")] public string Hello() => $"Hello {(HttpContext.User.Identity?.Name ?? "world")}!";}using Microsoft.AspNetCore.Authorization;using System.Globalization;using System.Security.Claims;namespace AuthRequirementsData.Authorization;class MinimumAgeAuthorizationHandler : AuthorizationHandler<MinimumAgeRequirement>{ private readonly ILogger<MinimumAgeAuthorizationHandler> _logger; public MinimumAgeAuthorizationHandler(ILogger<MinimumAgeAuthorizationHandler> logger) { _logger = logger; } // Check whether a given MinimumAgeRequirement is satisfied or not for a particular // context. protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, MinimumAgeRequirement requirement) { // Log as a warning so that it's very clear in sample output which authorization // policies(and requirements/handlers) are in use. _logger.LogWarning("Evaluating authorization requirement for age >= {age}", requirement.Age); // Check the user's age var dateOfBirthClaim = context.User.FindFirst(c => c.Type == ClaimTypes.DateOfBirth); if (dateOfBirthClaim != null) { // If the user has a date of birth claim, check their age var dateOfBirth = Convert.ToDateTime(dateOfBirthClaim.Value, CultureInfo.InvariantCulture); var age = DateTime.Now.Year - dateOfBirth.Year; if (dateOfBirth > DateTime.Now.AddYears(-age)) { // Adjust age if the user hasn't had a birthday yet this year. age--; } // If the user meets the age criterion, mark the authorization requirement // succeeded. if (age >= requirement.Age) { _logger.LogInformation("Minimum age authorization requirement {age} satisfied", requirement.Age); context.Succeed(requirement); } else { _logger.LogInformation("Current user's DateOfBirth claim ({dateOfBirth})" + " does not satisfy the minimum age authorization requirement {age}", dateOfBirthClaim.Value, requirement.Age); } } else { _logger.LogInformation("No DateOfBirth claim present"); } return Task.CompletedTask; }}The complete sample for .NET 7 or earlier is theOldStyleAuthRequirements sample app (dotnet/AspNetCore.Docs.Samples GitHub repository) (how to download).
.NET 8 introduces theIAuthorizationRequirementData interface. TheIAuthorizationRequirementData interface allows the attribute definition to specify the requirements associated with the authorization policy. UsingIAuthorizationRequirementData, the preceding custom authorization policy code can be written with fewer lines of code. The updatedProgram.cs file:
using AuthRequirementsData.Authorization;using Microsoft.AspNetCore.Authorization; var builder = WebApplication.CreateBuilder(); builder.Services.AddAuthentication().AddJwtBearer();builder.Services.AddAuthorization();builder.Services.AddControllers();-builder.Services.AddSingleton<IAuthorizationPolicyProvider, MinimumAgePolicyProvider>();builder.Services.AddSingleton<IAuthorizationHandler, MinimumAgeAuthorizationHandler>(); var app = builder.Build(); app.MapControllers(); app.Run();The updatedMinimumAgeAuthorizationHandler:
using Microsoft.AspNetCore.Authorization;using System.Globalization;using System.Security.Claims;namespace AuthRequirementsData.Authorization;class MinimumAgeAuthorizationHandler(ILogger<MinimumAgeAuthorizationHandler> logger) - : AuthorizationHandler<MinimumAgeRequirement>+ : AuthorizationHandler<MinimumAgeAuthorizeAttribute>{ protected override Task HandleRequirementAsync( AuthorizationHandlerContext context,- MinimumAgeRequirement requirement)+ MinimumAgeAuthorizeAttribute requirement) { ... }}The updated sample is theAuthRequirementsData sample app (dotnet/AspNetCore.Docs.Samples GitHub repository) (how to download).
For more information, seeCustom authorization policies with `IAuthorizationRequirementData`.
Swagger UI endpoints can now be secured in production environments by callingMapSwagger().RequireAuthorization. For more information, seeSecuring Swagger UI endpoints
The following sections describe miscellaneous new features in ASP.NET Core in .NET 8.
Keyed services refers to a mechanism for registering and retrieving Dependency Injection (DI) services using keys. A service is associated with a key by callingAddKeyedSingleton (orAddKeyedScoped orAddKeyedTransient) to register it. Access a registered service by specifying the key with the[FromKeyedServices] attribute. The following code shows how to use keyed services:
using Microsoft.AspNetCore.Mvc;using Microsoft.AspNetCore.SignalR;var builder = WebApplication.CreateBuilder(args);builder.Services.AddKeyedSingleton<ICache, BigCache>("big");builder.Services.AddKeyedSingleton<ICache, SmallCache>("small");builder.Services.AddControllers();var app = builder.Build();app.MapGet("/big", ([FromKeyedServices("big")] ICache bigCache) => bigCache.Get("date"));app.MapGet("/small", ([FromKeyedServices("small")] ICache smallCache) => smallCache.Get("date"));app.MapControllers();app.Run();public interface ICache{ object Get(string key);}public class BigCache : ICache{ public object Get(string key) => $"Resolving {key} from big cache.";}public class SmallCache : ICache{ public object Get(string key) => $"Resolving {key} from small cache.";}[ApiController][Route("/cache")]public class CustomServicesApiController : Controller{ [HttpGet("big-cache")] public ActionResult<object> GetOk([FromKeyedServices("big")] ICache cache) { return cache.Get("data-mvc"); }}public class MyHub : Hub{ public void Method([FromKeyedServices("small")] ICache cache) { Console.WriteLine(cache.Get("signalr")); }}Visual Studio project templates are now the recommended way to create single-page apps (SPAs) that have an ASP.NET Core backend. Templates are provided that create apps based on the JavaScript technologies, such asAngular,React, andVue. These templates:
For more information about the Visual Studio templates and how to access the legacy templates, seeOverview of Single Page Apps (SPAs) in ASP.NET Core
Attributes that previously required aType parameter are now available in cleaner generic variants. This is made possible by support forgeneric attributes in C# 11. For example, the syntax for annotating the response type of an action can be modified as follows:
[ApiController][Route("api/[controller]")]public class TodosController : Controller{ [HttpGet("/")]- [ProducesResponseType(typeof(Todo), StatusCodes.Status200OK)]+ [ProducesResponseType<Todo>(StatusCodes.Status200OK)] public Todo Get() => new Todo(1, "Write a sample", DateTime.Now, false);}Generic variants are supported for the following attributes:
[ProducesResponseType<T>][Produces<T>][MiddlewareFilter<T>][ModelBinder<T>][ModelMetadataType<T>][ServiceFilter<T>][TypeFilter<T>]The new analyzers shown in the following table are available in .NET 8.
| Diagnostic ID | Breaking or nonbreaking | Description |
|---|---|---|
| ASP0016 | Nonbreaking | Don't return a value from RequestDelegate |
| ASP0019 | Nonbreaking | Suggest using IHeaderDictionary.Append or the indexer |
| ASP0020 | Nonbreaking | Complex types referenced by route parameters must be parsable |
| ASP0021 | Nonbreaking | The return type of the BindAsync method must beValueTask<T> |
| ASP0022 | Nonbreaking | Route conflict detected between route handlers |
| ASP0023 | Nonbreaking | MVC: Route conflict detected between route handlers |
| ASP0024 | Nonbreaking | Route handler has multiple parameters with the[FromBody] attribute |
| ASP0025 | Nonbreaking | Use AddAuthorizationBuilder |
ASP.NET Core is built on routing. Minimal APIs, Web APIs, Razor Pages, and Blazor all use routes to customize how HTTP requests map to code.
In .NET 8 we've invested in a suite of new features to make routing easier to learn and use. These new features include:
For more information, seeRoute tooling in .NET 8.
Metrics are measurements reported over time and are most often used to monitor the health of an app and to generate alerts. For example, a counter that reports failed HTTP requests could be displayed in dashboards or generate alerts when failures pass a threshold.
This preview adds new metrics throughout ASP.NET Core usingSystem.Diagnostics.Metrics.Metrics is a modern API for reporting and collecting information about apps.
Metrics offers many improvements compared to existing event counters:
Metrics have been added for ASP.NET Core hosting, Kestrel, and SignalR. For more information, seeSystem.Diagnostics.Metrics.
IExceptionHandler is a new interface that gives the developer a callback for handling known exceptions in a central location.
IExceptionHandler implementations are registered by callingIServiceCollection.AddExceptionHandler<T>. Multiple implementations can be added, and they're called in the order registered. If an exception handler handles a request, it can returntrue to stop processing. If an exception isn't handled by any exception handler, then control falls back to the default behavior and options from the middleware.
For more information, seeIExceptionHandler.
Debug customization attributes have been added to types likeHttpContext,HttpRequest,HttpResponse,ClaimsPrincipal, andWebApplication. The enhanced debugger displays for these types make finding important information easier in an IDE's debugger. The following screenshots show the difference that these attributes make in the debugger's display ofHttpContext.
.NET 7:

.NET 8:

The debugger display forWebApplication highlights important information such as configured endpoints, middleware, andIConfiguration values.
.NET 7:

.NET 8:

For more information about debugging improvements in .NET 8, see:
IPNetwork.Parse andTryParseThe newParse andTryParse methods onIPNetwork add support for creating anIPNetwork by using an input string inCIDR notation or "slash notation".
Here are IPv4 examples:
// Using Parsevar network = IPNetwork.Parse("192.168.0.1/32");// Using TryParsebool success = IPNetwork.TryParse("192.168.0.1/32", out var network);// Constructor equivalentvar network = new IPNetwork(IPAddress.Parse("192.168.0.1"), 32);And here are examples for IPv6:
// Using Parsevar network = IPNetwork.Parse("2001:db8:3c4d::1/128");// Using TryParsebool success = IPNetwork.TryParse("2001:db8:3c4d::1/128", out var network);// Constructor equivalentvar network = new IPNetwork(IPAddress.Parse("2001:db8:3c4d::1"), 128);ASP.NET Core in .NET 8 adds support for using Redis as a distributed cache for output caching. Output caching is a feature that enables an app to cache the output of a minimal API endpoint, controller action, or Razor Page. For more information, seeOutput caching.
When routing matches an endpoint, it typically lets the rest of the middleware pipeline run before invoking the endpoint logic. Services can reduce resource usage by filtering out known requests early in the pipeline. Use theShortCircuit extension method to cause routing to invoke the endpoint logic immediately and then end the request. For example, a given route might not need to go through authentication or CORS middleware. The following example short-circuits requests that match the/short-circuit route:
app.MapGet("/short-circuit", () => "Short circuiting!").ShortCircuit();Use theMapShortCircuit method to set up short-circuiting for multiple routes at once, by passing to it a params array of URL prefixes. For example, browsers and bots often probe servers for well known paths likerobots.txt andfavicon.ico. If the app doesn't have those files, one line of code can configure both routes:
app.MapShortCircuit(404, "robots.txt", "favicon.ico");For more information, seeShort-circuit middleware after routing.
The HTTP logging middleware has several new capabilities:
For more information, seeHTTP logging in .NET and ASP.NET Core.
In .NET 7, theProblemDetails service was introduced to improve the experience for generating error responses that comply with theProblemDetails specification. In .NET 8, a new API was added to make it easier to implement fallback behavior ifIProblemDetailsService isn't able to generateProblemDetails. The following example illustrates use of the newTryWriteAsync API:
var builder = WebApplication.CreateBuilder(args);builder.Services.AddProblemDetails();var app = builder.Build();app.UseExceptionHandler(exceptionHandlerApp =>{ exceptionHandlerApp.Run(async httpContext => { var pds = httpContext.RequestServices.GetService<IProblemDetailsService>(); if (pds == null || !await pds.TryWriteAsync(new() { HttpContext = httpContext })) { // Fallback behavior await httpContext.Response.WriteAsync("Fallback: An error occurred."); } });});app.MapGet("/exception", () =>{ throw new InvalidOperationException("Sample Exception");});app.MapGet("/", () => "Test by calling /exception");app.Run();For more information, seeIProblemDetailsService fallback
Use the articles inBreaking changes in .NET to find breaking changes that might apply when upgrading an app to a newer version of .NET.
Was this page helpful?
Need help with this topic?
Want to try using Ask Learn to clarify or guide you through this topic?
Was this page helpful?
Want to try using Ask Learn to clarify or guide you through this topic?