Migrate from ASP.NET Core 2.2 to 3.0
By Scott Addie and Rick Anderson
This article explains how to update an existing ASP.NET Core 2.2 project to ASP.NET Core 3.0. It might be helpful to create a new ASP.NET Core 3.0 project to:
- Compare with the ASP.NET Core 2.2 code.
- Copy the relevant changes to your ASP.NET Core 3.0 project.
Prerequisites
- Visual Studio 2019 with the ASP.NET and web development workload
- .NET Core 3.0 SDK
Update .NET Core SDK version in global.json
If your solution relies upon a global.json file to target a specific .NET Core SDK version, update its version
property to the 3.0 version installed on your machine:
{
"sdk": {
"version": "3.0.100"
}
}
Update the project file
Update the Target Framework
ASP.NET Core 3.0 and later only run on .NET Core. Set the Target Framework Moniker (TFM) to netcoreapp3.0
:
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp3.0</TargetFramework>
</PropertyGroup>
</Project>
Remove obsolete package references
A large number of NuGet packages aren't produced for ASP.NET Core 3.0. Such package references should be removed from your project file. Consider the following project file for an ASP.NET Core 2.2 web app:
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp2.2</TargetFramework>
<AspNetCoreHostingModel>InProcess</AspNetCoreHostingModel>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.App"/>
<PackageReference Include="Microsoft.AspNetCore.Razor.Design" Version="2.2.0" PrivateAssets="All" />
</ItemGroup>
</Project>
The updated project file for ASP.NET Core 3.0:
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp3.0</TargetFramework>
</PropertyGroup>
</Project>
The updated ASP.NET Core 3.0 project file:
In the
<PropertyGroup>
:- Updates the TFM to
netcoreapp3.0
- Removes the
<AspNetCoreHostingModel>
element. For more information, see In-process hosting model in this document.
- Updates the TFM to
In the
<ItemGroup>
:Microsoft.AspNetCore.App
is removed. For more information, see Framework reference in this document.Microsoft.AspNetCore.Razor.Design
is removed and in the following list of packages no longer being produced.
To see the full list of packages that are no longer produced, select the following expand list:
Click to expand the list of packages no longer being produced
- Microsoft.AspNetCore
- Microsoft.AspNetCore.All
- Microsoft.AspNetCore.App
- Microsoft.AspNetCore.Antiforgery
- Microsoft.AspNetCore.Authentication
- Microsoft.AspNetCore.Authentication.Abstractions
- Microsoft.AspNetCore.Authentication.Cookies
- Microsoft.AspNetCore.Authentication.Core
- Microsoft.AspNetCore.Authentication.OAuth
- Microsoft.AspNetCore.Authorization.Policy
- Microsoft.AspNetCore.CookiePolicy
- Microsoft.AspNetCore.Cors
- Microsoft.AspNetCore.Diagnostics
- Microsoft.AspNetCore.Diagnostics.HealthChecks
- Microsoft.AspNetCore.HostFiltering
- Microsoft.AspNetCore.Hosting
- Microsoft.AspNetCore.Hosting.Abstractions
- Microsoft.AspNetCore.Hosting.Server.Abstractions
- Microsoft.AspNetCore.Http
- Microsoft.AspNetCore.Http.Abstractions
- Microsoft.AspNetCore.Http.Connections
- Microsoft.AspNetCore.Http.Extensions
- Microsoft.AspNetCore.HttpOverrides
- Microsoft.AspNetCore.HttpsPolicy
- Microsoft.AspNetCore.Identity
- Microsoft.AspNetCore.Localization
- Microsoft.AspNetCore.Localization.Routing
- Microsoft.AspNetCore.Mvc
- Microsoft.AspNetCore.Mvc.Abstractions
- Microsoft.AspNetCore.Mvc.Analyzers
- Microsoft.AspNetCore.Mvc.ApiExplorer
- Microsoft.AspNetCore.Mvc.Api.Analyzers
- Microsoft.AspNetCore.Mvc.Core
- Microsoft.AspNetCore.Mvc.Cors
- Microsoft.AspNetCore.Mvc.DataAnnotations
- Microsoft.AspNetCore.Mvc.Formatters.Json
- Microsoft.AspNetCore.Mvc.Formatters.Xml
- Microsoft.AspNetCore.Mvc.Localization
- Microsoft.AspNetCore.Mvc.Razor
- Microsoft.AspNetCore.Mvc.Razor.ViewCompilation
- Microsoft.AspNetCore.Mvc.RazorPages
- Microsoft.AspNetCore.Mvc.TagHelpers
- Microsoft.AspNetCore.Mvc.ViewFeatures
- Microsoft.AspNetCore.Razor
- Microsoft.AspNetCore.Razor.Runtime
- Microsoft.AspNetCore.Razor.Design
- Microsoft.AspNetCore.ResponseCaching
- Microsoft.AspNetCore.ResponseCaching.Abstractions
- Microsoft.AspNetCore.ResponseCompression
- Microsoft.AspNetCore.Rewrite
- Microsoft.AspNetCore.Routing
- Microsoft.AspNetCore.Routing.Abstractions
- Microsoft.AspNetCore.Server.HttpSys
- Microsoft.AspNetCore.Server.IIS
- Microsoft.AspNetCore.Server.IISIntegration
- Microsoft.AspNetCore.Server.Kestrel
- Microsoft.AspNetCore.Server.Kestrel.Core
- Microsoft.AspNetCore.Server.Kestrel.Https
- Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions
- Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets
- Microsoft.AspNetCore.Session
- Microsoft.AspNetCore.SignalR
- Microsoft.AspNetCore.SignalR.Core
- Microsoft.AspNetCore.StaticFiles
- Microsoft.AspNetCore.WebSockets
- Microsoft.AspNetCore.WebUtilities
- Microsoft.Net.Http.Headers
Review breaking changes
Framework reference
Features of ASP.NET Core that were available through one of the packages listed above are available as part of the Microsoft.AspNetCore.App
shared framework. The shared framework is the set of assemblies (.dll files) that are installed on the machine and includes a runtime component and a targeting pack. For more information, see The shared framework.
Projects that target the
Microsoft.NET.Sdk.Web
SDK implicitly reference theMicrosoft.AspNetCore.App
framework.No additional references are required for these projects:
<Project Sdk="Microsoft.NET.Sdk.Web"> <PropertyGroup> <TargetFramework>netcoreapp3.0</TargetFramework> </PropertyGroup> ... </Project>
Projects that target
Microsoft.NET.Sdk
orMicrosoft.NET.Sdk.Razor
SDK, should add an explicitFrameworkReference
toMicrosoft.AspNetCore.App
:<Project Sdk="Microsoft.NET.Sdk.Razor"> <PropertyGroup> <TargetFramework>netcoreapp3.0</TargetFramework> </PropertyGroup> <ItemGroup> <FrameworkReference Include="Microsoft.AspNetCore.App" /> </ItemGroup> ... </Project>
Framework-dependent builds using Docker
Framework-dependent builds of console apps that use a package that depends on the ASP.NET Core shared framework may give the following runtime error:
It was not possible to find any compatible framework version
The specified framework 'Microsoft.AspNetCore.App', version '3.0.0' was not found.
- No frameworks were found.
Microsoft.AspNetCore.App
is the shared framework containing the ASP.NET Core runtime and is only present on the dotnet/core/aspnet
Docker image. The 3.0 SDK reduces the size of framework-dependent builds using ASP.NET Core by not including duplicate copies of libraries that are available in the shared framework. This is a potential savings of up to 18 MB, but it requires that the ASP.NET Core runtime be present / installed to run the app.
To determine if the app has a dependency (either direct or indirect) on the ASP.NET Core shared framework, examine the runtimeconfig.json
file generated during a build/publish of your app. The following JSON file shows a dependency on the ASP.NET Core shared framework:
{
"runtimeOptions": {
"tfm": "netcoreapp3.0",
"framework": {
"name": "Microsoft.AspNetCore.App",
"version": "3.0.0"
},
"configProperties": {
"System.GC.Server": true
}
}
}
If your app is using Docker, use a base image that includes ASP.NET Core 3.0. For example, docker pull mcr.microsoft.com/dotnet/core/aspnet:3.0
.
Add package references for removed assemblies
ASP.NET Core 3.0 removes some assemblies that were previously part of the Microsoft.AspNetCore.App
package reference. To visualize which assemblies were removed, compare the two shared framework folders. For example, a comparison of versions 2.2.7 and 3.0.0:
To continue using features provided by the removed assemblies, reference the 3.0 versions of the corresponding packages:
A template-generated web app with Individual User Accounts requires adding the following packages:
<Project Sdk="Microsoft.NET.Sdk.Web"> <PropertyGroup> <TargetFramework>netcoreapp3.0</TargetFramework> <UserSecretsId>My-secret</UserSecretsId> </PropertyGroup> <ItemGroup> <PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="3.0.0" /> <PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="3.0.0" /> <PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="3.0.0" /> <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="3.0.0" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="3.0.0" /> </ItemGroup> </Project>
-
For more information on referencing the database provider-specific package, see Database Providers.
Identity UI
Support for Identity UI can be added by referencing the Microsoft.AspNetCore.Identity.UI package.
SPA Services
Authentication: Support for third-party authentication flows are available as NuGet packages:
- Facebook OAuth (Microsoft.AspNetCore.Authentication.Facebook)
- Google OAuth (Microsoft.AspNetCore.Authentication.Google)
- Microsoft Account authentication (Microsoft.AspNetCore.Authentication.MicrosoftAccount)
- OpenID Connect authentication (Microsoft.AspNetCore.Authentication.OpenIdConnect)
- OpenID Connect bearer token (Microsoft.AspNetCore.Authentication.JwtBearer)
- Twitter OAuth (Microsoft.AspNetCore.Authentication.Twitter)
- WsFederation authentication (Microsoft.AspNetCore.Authentication.WsFederation)
Formatting and content negotiation support for
System.Net.HttpClient
: The Microsoft.AspNet.WebApi.Client NuGet package provides useful extensibility toSystem.Net.HttpClient
with APIs such asReadAsAsync
andPostJsonAsync
. However, this package depends onNewtonsoft.Json
, notSystem.Text.Json
. That means, for example, that serialization property names specified byJsonPropertyNameAttribute
(System.Text.Json
) are ignored. There's a newer NuGet package that contains similar extension methods but usesSystem.Text.Json
: System.Net.Http.Json.Razor runtime compilation: Support for runtime compilation of Razor views and pages is now part of Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation.
MVC
Newtonsoft.Json
(Json.NET) support: Support for using MVC withNewtonsoft.Json
is now part ofMicrosoft.AspNetCore.Mvc.NewtonsoftJson
.
Startup changes
The following image shows the deleted and changed lines in an ASP.NET Core 2.2 Razor Pages Web app:
In the preceding image, deleted code is shown in red. The deleted code doesn't show cookie options code, which was deleted prior to comparing the files.
The following image shows the added and changed lines in an ASP.NET Core 3.0 Razor Pages Web app:
In the preceding image, added code is shown in green. For information on the following changes:
services.AddMvc
toservices.AddRazorPages
, see MVC service registration in this document.CompatibilityVersion
, see Compatibility version for ASP.NET Core MVC.IHostingEnvironment
toIWebHostEnvironment
, see this GitHub announcement.app.UseAuthorization
was added to the templates to show the order authorization middleware must be added. If the app doesn't use authorization, you can safely remove the call toapp.UseAuthorization
.app.UseEndpoints
, see Razor Pages or Migrate Startup.Configure in this document.
Analyzer support
Projects that target Microsoft.NET.Sdk.Web
implicitly reference analyzers previously shipped as part of the Microsoft.AspNetCore.Mvc.Analyzers package. No additional references are required to enable these.
If your app uses API analyzers previously shipped using the Microsoft.AspNetCore.Mvc.Api.Analyzers package, edit your project file to reference the analyzers shipped as part of the .NET Core Web SDK:
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp3.0</TargetFramework>
<IncludeOpenAPIAnalyzers>true</IncludeOpenAPIAnalyzers>
</PropertyGroup>
...
</Project>
Razor class library
Razor class library projects that provide UI components for MVC must set the AddRazorSupportForMvc
property in the project file:
<PropertyGroup>
<AddRazorSupportForMvc>true</AddRazorSupportForMvc>
</PropertyGroup>
In-process hosting model
Projects default to the in-process hosting model in ASP.NET Core 3.0 or later. You may optionally remove the <AspNetCoreHostingModel>
property in the project file if its value is InProcess
.
Kestrel
Configuration
Migrate Kestrel configuration to the web host builder provided by ConfigureWebHostDefaults
(Program.cs
):
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.ConfigureKestrel(serverOptions =>
{
// Set properties and call methods on options
})
.UseStartup<Startup>();
});
If the app creates the host manually with ConfigureWebHost
instead of ConfigureWebHostDefaults
, call UseKestrel
on the web host builder:
public static void Main(string[] args)
{
var host = new HostBuilder()
.UseContentRoot(Directory.GetCurrentDirectory())
.ConfigureWebHost(webBuilder =>
{
webBuilder.UseKestrel(serverOptions =>
{
// Set properties and call methods on options
})
.UseIISIntegration()
.UseStartup<Startup>();
})
.Build();
host.Run();
}
Connection Middleware replaces Connection Adapters
Connection Adapters (Microsoft.AspNetCore.Server.Kestrel.Core.Adapter.Internal.IConnectionAdapter
) have been removed from Kestrel. Replace Connection Adapters with Connection Middleware. Connection Middleware is similar to HTTP Middleware in the ASP.NET Core pipeline but for lower-level connections. HTTPS and connection logging:
- Have been moved from Connection Adapters to Connection Middleware.
- These extension methods work as in previous versions of ASP.NET Core.
For more information, see the TlsFilterConnectionHandler example in the ListenOptions.Protocols section of the Kestrel article.
Transport abstractions moved and made public
The Kestrel transport layer has been exposed as a public interface in Connections.Abstractions
. As part of these updates:
Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions
and associated types have been removed.- NoDelay was moved from ListenOptions to the transport options.
Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal.SchedulingMode
was removed from KestrelServerOptions.
For more information, see the following GitHub resources:
- Client/server networking abstractions (dotnet/AspNetCore #10308)
- Implement new bedrock listener abstraction and re-plat Kestrel on top (dotnet/AspNetCore #10321)
Kestrel Request trailer headers
For apps that target earlier versions of ASP.NET Core:
- Kestrel adds HTTP/1.1 chunked trailer headers into the request headers collection.
- Trailers are available after the request body is read to the end.
This causes some concerns about ambiguity between headers and trailers, so the trailers have been moved to a new collection (RequestTrailerExtensions
) in 3.0.
HTTP/2 request trailers are:
- Not available in ASP.NET Core 2.2.
- Available in 3.0 as
RequestTrailerExtensions
.
New request extension methods are present to access these trailers. As with HTTP/1.1, trailers are available after the request body is read to the end.
For the 3.0 release, the following RequestTrailerExtensions
methods are available:
GetDeclaredTrailers
: Gets the requestTrailer
header that lists which trailers to expect after the body.SupportsTrailers
: Indicates if the request supports receiving trailer headers.CheckTrailersAvailable
: Checks if the request supports trailers and if they're available to be read. This check doesn't assume that there are trailers to read. There might be no trailers to read even iftrue
is returned by this method.GetTrailer
: Gets the requested trailing header from the response. CheckSupportsTrailers
before callingGetTrailer
, or a NotSupportedException may occur if the request doesn't support trailing headers.
For more information, see Put request trailers in a separate collection (dotnet/AspNetCore #10410).
AllowSynchronousIO disabled
AllowSynchronousIO
enables or disables synchronous I/O APIs, such as HttpRequest.Body.Read
, HttpResponse.Body.Write
, and Stream.Flush
. These APIs are a source of thread starvation leading to app crashes. In 3.0, AllowSynchronousIO
is disabled by default. For more information, see the Synchronous I/O section in the Kestrel article.
If synchronous I/O is needed, it can be enabled by configuring the AllowSynchronousIO
option on the server being used (when calling ConfigureKestrel
, for example, if using Kestrel). Note that servers (Kestrel, HttpSys, TestServer, etc.) all have their own AllowSynchronousIO
option that won't affect other servers. Synchronous I/O can be enabled for all servers on a per-request basis using the IHttpBodyControlFeature.AllowSynchronousIO
option:
var syncIOFeature = HttpContext.Features.Get<IHttpBodyControlFeature>();
if (syncIOFeature != null)
{
syncIOFeature.AllowSynchronousIO = true;
}
If you have trouble with TextWriter implementations or other streams that call synchronous APIs in Dispose, call the new DisposeAsync API instead.
For more information, see [Announcement] AllowSynchronousIO disabled in all servers (dotnet/AspNetCore #7644).
Output formatter buffering
Newtonsoft.Json, XmlSerializer, and DataContractSerializer based output formatters only support synchronous serialization. To allow these formatters to work with the AllowSynchronousIO restrictions of the server, MVC buffers the output of these formatters before writing to disk. As a result of buffering, MVC will include the Content-Length header when responding using these formatters.
System.Text.Json supports asynchronous serialization and consequently the System.Text.Json
based formatter does not buffer. Consider using this formatter for improved performance.
To disable buffering, applications can configure SuppressOutputFormatterBuffering in their startup:
services.AddControllers(options => options.SuppressOutputFormatterBuffering = true)
Note that this may result in the application throwing a runtime exception if AllowSynchronousIO
isn't also configured.
Microsoft.AspNetCore.Server.Kestrel.Https assembly removed
In ASP.NET Core 2.1, the contents of Microsoft.AspNetCore.Server.Kestrel.Https.dll were moved to Microsoft.AspNetCore.Server.Kestrel.Core.dll. This was a non-breaking update using TypeForwardedTo
attributes. For 3.0, the empty Microsoft.AspNetCore.Server.Kestrel.Https.dll assembly and the NuGet package have been removed.
Libraries referencing Microsoft.AspNetCore.Server.Kestrel.Https should update ASP.NET Core dependencies to 2.1 or later.
Apps and libraries targeting ASP.NET Core 2.1 or later should remove any direct references to the Microsoft.AspNetCore.Server.Kestrel.Https package.
Newtonsoft.Json (Json.NET) support
As part of the work to improve the ASP.NET Core shared framework, Newtonsoft.Json (Json.NET) has been removed from the ASP.NET Core shared framework.
The default JSON serializer for ASP.NET Core is now System.Text.Json, which is new in .NET Core 3.0. Consider using System.Text.Json
when possible. It's high-performance and doesn't require an additional library dependency. However, since System.Text.Json
is new, it might currently be missing features that your app needs. For more information, see How to migrate from Newtonsoft.Json to System.Text.Json.
Use Newtonsoft.Json in an ASP.NET Core 3.0 SignalR project
Install the Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson NuGet package.
On the client, chain an
AddNewtonsoftJsonProtocol
method call to theHubConnectionBuilder
instance:new HubConnectionBuilder() .WithUrl("/chathub") .AddNewtonsoftJsonProtocol(...) .Build();
On the server, chain an
AddNewtonsoftJsonProtocol
method call to theAddSignalR
method call inStartup.ConfigureServices
:services.AddSignalR() .AddNewtonsoftJsonProtocol(...);
Use Newtonsoft.Json in an ASP.NET Core 3.0 MVC project
Install the
Microsoft.AspNetCore.Mvc.NewtonsoftJson
package.Update
Startup.ConfigureServices
to callAddNewtonsoftJson
.services.AddMvc() .AddNewtonsoftJson();
AddNewtonsoftJson
is compatible with the new MVC service registration methods:AddRazorPages
AddControllersWithViews
AddControllers
services.AddControllers() .AddNewtonsoftJson();
Newtonsoft.Json
settings can be set in the call toAddNewtonsoftJson
:services.AddMvc() .AddNewtonsoftJson(options => options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver());
Note: If the
AddNewtonsoftJson
method isn't available, make sure that you installed theMicrosoft.AspNetCore.Mvc.NewtonsoftJson
package. A common error is to install the Newtonsoft.Json package instead of theMicrosoft.AspNetCore.Mvc.NewtonsoftJson
package.
For more information, see Add Newtonsoft.Json-based JSON format support.
MVC service registration
ASP.NET Core 3.0 adds new options for registering MVC scenarios inside Startup.ConfigureServices
.
Three new top-level extension methods related to MVC scenarios on IServiceCollection
are available. Templates use these new methods instead of AddMvc
. However, AddMvc
continues to behave as it has in previous releases.
The following example adds support for controllers and API-related features, but not views or pages. The API template uses this code:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
}
The following example adds support for controllers, API-related features, and views, but not pages. The Web Application (MVC) template uses this code:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
}
The following example adds support for Razor Pages and minimal controller support. The Web Application template uses this code:
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
}
The new methods can also be combined. The following example is equivalent to calling AddMvc
in ASP.NET Core 2.2:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddRazorPages();
}
Routing startup code
If an app calls UseMvc
or UseSignalR
, migrate the app to Endpoint Routing if possible. To improve Endpoint Routing compatibility with previous versions of MVC, we've reverted some of the changes in URL generation introduced in ASP.NET Core 2.2. If you experienced problems using Endpoint Routing in 2.2, expect improvements in ASP.NET Core 3.0 with the following exceptions:
- If the app implements
IRouter
or inherits fromRoute
, use DynamicRouteValuesTransformer as the replacement. - If the app directly accesses
RouteData.Routers
inside MVC to parse URLs, you can replace this with use of LinkParser.ParsePathByEndpointName.- Define the route with a route name.
- Use
LinkParser.ParsePathByEndpointName
and pass in the desired route name.
Endpoint Routing supports the same route pattern syntax and route pattern authoring features as IRouter
. Endpoint Routing supports IRouteConstraint
. Endpoint routing supports [Route]
, [HttpGet]
, and the other MVC routing attributes.
For most applications, only Startup
requires changes.
Migrate Startup.Configure
General advice:
Add
UseRouting
.If the app calls
UseStaticFiles
, placeUseStaticFiles
beforeUseRouting
.If the app uses authentication/authorization features such as
AuthorizePage
or[Authorize]
, place the call toUseAuthentication
andUseAuthorization
: after,UseRouting
andUseCors
, but beforeUseEndpoints
:public void Configure(IApplicationBuilder app) { ... app.UseStaticFiles(); app.UseRouting(); app.UseCors(); app.UseAuthentication(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); });
Replace
UseMvc
orUseSignalR
withUseEndpoints
.If the app uses CORS scenarios, such as
[EnableCors]
, place the call toUseCors
before any other middleware that use CORS (for example, placeUseCors
beforeUseAuthentication
,UseAuthorization
, andUseEndpoints
).Replace
IHostingEnvironment
withIWebHostEnvironment
and add ausing
statement for the Microsoft.AspNetCore.Hosting namespace.Replace
IApplicationLifetime
with IHostApplicationLifetime (Microsoft.Extensions.Hosting namespace).Replace
EnvironmentName
with Environments (Microsoft.Extensions.Hosting namespace).
The following code is an example of Startup.Configure
in a typical ASP.NET Core 2.2 app:
public void Configure(IApplicationBuilder app)
{
...
app.UseStaticFiles();
app.UseAuthentication();
app.UseSignalR(hubs =>
{
hubs.MapHub<ChatHub>("/chat");
});
app.UseMvc(routes =>
{
routes.MapRoute("default", "{controller=Home}/{action=Index}/{id?}");
});
}
After updating the previous Startup.Configure
code:
public void Configure(IApplicationBuilder app)
{
...
app.UseStaticFiles();
app.UseRouting();
app.UseCors();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapHub<ChatHub>("/chat");
endpoints.MapControllerRoute("default", "{controller=Home}/{action=Index}/{id?}");
});
}
Warning
For most apps, calls to UseAuthentication
, UseAuthorization
, and UseCors
must appear between the calls to UseRouting
and UseEndpoints
to be effective.
Health Checks
Health Checks use endpoint routing with the Generic Host. In Startup.Configure
, call MapHealthChecks
on the endpoint builder with the endpoint URL or relative path:
app.UseEndpoints(endpoints =>
{
endpoints.MapHealthChecks("/health");
});
Health Checks endpoints can:
- Specify one or more permitted hosts/ports.
- Require authorization.
- Require CORS.
For more information, see Health checks in ASP.NET Core.
Security middleware guidance
Support for authorization and CORS is unified around the middleware approach. This allows use of the same middleware and functionality across these scenarios. An updated authorization middleware is provided in this release, and CORS Middleware is enhanced so that it can understand the attributes used by MVC controllers.
CORS
Previously, CORS could be difficult to configure. Middleware was provided for use in some use cases, but MVC filters were intended to be used without the middleware in other use cases. With ASP.NET Core 3.0, we recommend that all apps that require CORS use the CORS Middleware in tandem with Endpoint Routing. UseCors
can be provided with a default policy, and [EnableCors]
and [DisableCors]
attributes can be used to override the default policy where required.
In the following example:
- CORS is enabled for all endpoints with the
default
named policy. - The
MyController
class disables CORS with the[DisableCors]
attribute.
public void Configure(IApplicationBuilder app)
{
...
app.UseRouting();
app.UseCors("default");
app.UseEndpoints(endpoints =>
{
endpoints.MapDefaultControllerRoute();
});
}
[DisableCors]
public class MyController : ControllerBase
{
...
}
Authorization
In earlier versions of ASP.NET Core, authorization support was provided via the [Authorize]
attribute. Authorization middleware wasn't available. In ASP.NET Core 3.0, authorization middleware is required. We recommend placing the ASP.NET Core Authorization Middleware (UseAuthorization
) immediately after UseAuthentication
. The Authorization Middleware can also be configured with a default policy, which can be overridden.
In ASP.NET Core 3.0 or later, UseAuthorization
is called in Startup.Configure
, and the following HomeController
requires a signed in user:
public void Configure(IApplicationBuilder app)
{
...
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapDefaultControllerRoute();
});
}
public class HomeController : Controller
{
[Authorize]
public IActionResult BuyWidgets()
{
...
}
}
When using endpoint routing, we recommend against configuring AuthorizeFilter and instead relying on the Authorization middleware. If the app uses an AuthorizeFilter
as a global filter in MVC, we recommend refactoring the code to provide a policy in the call to AddAuthorization
.
The DefaultPolicy
is initially configured to require authentication, so no additional configuration is required. In the following example, MVC endpoints are marked as RequireAuthorization
so that all requests must be authorized based on the DefaultPolicy
. However, the HomeController
allows access without the user signing into the app due to [AllowAnonymous]
:
public void Configure(IApplicationBuilder app)
{
...
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapDefaultControllerRoute().RequireAuthorization();
});
}
[AllowAnonymous]
public class HomeController : Controller
{
...
}
Authorization for specific endpoints
Authorization can also be configured for specific classes of endpoints. The following code is an example of converting an MVC app that configured a global AuthorizeFilter
to an app with a specific policy requiring authorization:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
static readonly string _RequireAuthenticatedUserPolicy =
"RequireAuthenticatedUserPolicy";
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")));
services.AddDefaultIdentity<IdentityUser>(
options => options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<ApplicationDbContext>();
// Pre 3.0:
// services.AddMvc(options => options.Filters.Add(new AuthorizeFilter(...));
services.AddControllersWithViews();
services.AddRazorPages();
services.AddAuthorization(o => o.AddPolicy(_RequireAuthenticatedUserPolicy,
builder => builder.RequireAuthenticatedUser()));
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapDefaultControllerRoute()
.RequireAuthorization(_RequireAuthenticatedUserPolicy);
endpoints.MapRazorPages();
});
}
}
Policies can also be customized. The DefaultPolicy
is configured to require authentication:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")));
services.AddDefaultIdentity<IdentityUser>(
options => options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<ApplicationDbContext>();
services.AddControllersWithViews();
services.AddRazorPages();
services.AddAuthorization(options =>
{
options.DefaultPolicy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapDefaultControllerRoute().RequireAuthorization();
endpoints.MapRazorPages();
});
}
}
[AllowAnonymous]
public class HomeController : Controller
{
Alternatively, all endpoints can be configured to require authorization without [Authorize]
or RequireAuthorization
by configuring a FallbackPolicy
. The FallbackPolicy
is different from the DefaultPolicy
. The DefaultPolicy
is triggered by [Authorize]
or RequireAuthorization
, while the FallbackPolicy
is triggered when no other policy is set. FallbackPolicy
is initially configured to allow requests without authorization.
The following example is the same as the preceding DefaultPolicy
example but uses the FallbackPolicy
to always require authentication on all endpoints except when [AllowAnonymous]
is specified:
public void ConfigureServices(IServiceCollection services)
{
...
services.AddAuthorization(options =>
{
options.FallbackPolicy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
});
}
public void Configure(IApplicationBuilder app)
{
...
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapDefaultControllerRoute();
});
}
[AllowAnonymous]
public class HomeController : Controller
{
...
}
Authorization by middleware works without the framework having any specific knowledge of authorization. For instance, health checks has no specific knowledge of authorization, but health checks can have a configurable authorization policy applied by the middleware.
Additionally, each endpoint can customize its authorization requirements. In the following example, UseAuthorization
processes authorization with the DefaultPolicy
, but the /healthz
health check endpoint requires an admin
user:
public void Configure(IApplicationBuilder app)
{
...
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints
.MapHealthChecks("/healthz")
.RequireAuthorization(new AuthorizeAttribute(){ Roles = "admin", });
});
}
Protection is implemented for some scenarios. Endpoints Middleware throws an exception if an authorization or CORS policy is skipped due to missing middleware. Analyzer support to provide additional feedback about misconfiguration is in progress.
Custom authorization handlers
If the app uses custom authorization handlers, endpoint routing passes a different resource type to handlers than MVC. Handlers that expect the authorization handler context resource to be of type AuthorizationFilterContext (the resource type provided by MVC filters) will need to be updated to handle resources of type RouteEndpoint (the resource type given to authorization handlers by endpoint routing).
MVC still uses AuthorizationFilterContext
resources, so if the app uses MVC authorization filters along with endpoint routing authorization, it may be necessary to handle both types of resources.
SignalR
Mapping of SignalR hubs now takes place inside UseEndpoints
.
Map each hub with MapHub
. As in previous versions, each hub is explicitly listed.
In the following example, support for the ChatHub
SignalR hub is added:
public void Configure(IApplicationBuilder app)
{
...
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapHub<ChatHub>();
});
}
There is a new option for controlling message size limits from clients. For example, in Startup.ConfigureServices
:
services.AddSignalR(hubOptions =>
{
hubOptions.MaximumReceiveMessageSize = 32768;
});
In ASP.NET Core 2.2, you could set the TransportMaxBufferSize
and that would effectively control the maximum message size. In ASP.NET Core 3.0, that option now only controls the maximum size before backpressure is observed.
SignalR assemblies in shared framework
ASP.NET Core SignalR server-side assemblies are now installed with the .NET Core SDK. For more information, see Remove obsolete package references in this document.
MVC controllers
Mapping of controllers now takes place inside UseEndpoints
.
Add MapControllers
if the app uses attribute routing. Since routing includes support for many frameworks in ASP.NET Core 3.0 or later, adding attribute-routed controllers is opt-in.
Replace the following:
MapRoute
withMapControllerRoute
MapAreaRoute
withMapAreaControllerRoute
Since routing now includes support for more than just MVC, the terminology has changed to make these methods clearly state what they do. Conventional routes such as MapControllerRoute
/MapAreaControllerRoute
/MapDefaultControllerRoute
are applied in the order that they're added. Place more specific routes (such as routes for an area) first.
In the following example:
MapControllers
adds support for attribute-routed controllers.MapAreaControllerRoute
adds a conventional route for controllers in an area.MapControllerRoute
adds a conventional route for controllers.
public void Configure(IApplicationBuilder app)
{
...
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapAreaControllerRoute(
"admin",
"admin",
"Admin/{controller=Home}/{action=Index}/{id?}");
endpoints.MapControllerRoute(
"default", "{controller=Home}/{action=Index}/{id?}");
});
}
Async suffix removal from controller action names
In ASP.NET Core 3.0, ASP.NET Core MVC removes the Async
suffix from controller action names. Both routing and link generation are impacted by this new default. For example:
public class ProductsController : Controller
{
public async Task<IActionResult> ListAsync()
{
var model = await _dbContext.Products.ToListAsync();
return View(model);
}
}
Prior to ASP.NET Core 3.0:
The preceding action could be accessed at the Products/ListAsync route.
Link generation required specifying the
Async
suffix. For example:<a asp-controller="Products" asp-action="ListAsync">List</a>
In ASP.NET Core 3.0:
The preceding action can be accessed at the Products/List route.
Link generation doesn't require specifying the
Async
suffix. For example:<a asp-controller="Products" asp-action="List">List</a>
This change doesn't affect names specified using the [ActionName]
attribute. The default behavior can be disabled with the following code in Startup.ConfigureServices
:
services.AddMvc(options =>
options.SuppressAsyncSuffixInActionNames = false);
Changes to link generation
There are some differences in link generation (using Url.Link
and similar APIs, for example). These include:
- By default, when using endpoint routing, casing of route parameters in generated URIs is not necessarily preserved. This behavior can be controlled with the
IOutboundParameterTransformer
interface. - Generating a URI for an invalid route (a controller/action or page that doesn't exist) will produce an empty string under endpoint routing instead of producing an invalid URI.
- Ambient values (route parameters from the current context) are not automatically used in link generation with endpoint routing. Previously, when generating a link to another action (or page), unspecified route values would be inferred from the current routes ambient values. When using endpoint routing, all route parameters must be specified explicitly during link generation.
Razor Pages
Mapping Razor Pages now takes place inside UseEndpoints
.
Add MapRazorPages
if the app uses Razor Pages. Since Endpoint Routing includes support for many frameworks, adding Razor Pages is now opt-in.
In the following Startup.Configure
method, MapRazorPages
adds support for Razor Pages:
public void Configure(IApplicationBuilder app)
{
...
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
});
}
Use MVC without Endpoint Routing
Using MVC via UseMvc
or UseMvcWithDefaultRoute
in ASP.NET Core 3.0 requires an explicit opt-in inside Startup.ConfigureServices
. This is required because MVC must know whether it can rely on the authorization and CORS Middleware during initialization. An analyzer is provided that warns if the app attempts to use an unsupported configuration.
If the app requires legacy IRouter
support, disable EnableEndpointRouting
using any of the following approaches in Startup.ConfigureServices
:
services.AddMvc(options => options.EnableEndpointRouting = false);
services.AddControllers(options => options.EnableEndpointRouting = false);
services.AddControllersWithViews(options => options.EnableEndpointRouting = false);
services.AddRazorPages().AddMvcOptions(options => options.EnableEndpointRouting = false);
Health checks
Health checks can be used as a router-ware with Endpoint Routing.
Add MapHealthChecks
to use health checks with Endpoint Routing. The MapHealthChecks
method accepts arguments similar to UseHealthChecks
. The advantage of using MapHealthChecks
over UseHealthChecks
is the ability to apply authorization and to have greater fine-grained control over the matching policy.
In the following example, MapHealthChecks
is called for a health check endpoint at /healthz
:
public void Configure(IApplicationBuilder app)
{
...
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapHealthChecks("/healthz", new HealthCheckOptions() { });
});
}
HostBuilder replaces WebHostBuilder
The ASP.NET Core 3.0 templates use Generic Host. Previous versions used Web Host. The following code shows the ASP.NET Core 3.0 template generated Program
class:
// requires using Microsoft.AspNetCore.Hosting;
// requires using Microsoft.Extensions.Hosting;
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
The following code shows the ASP.NET Core 2.2 template-generated Program
class:
public class Program
{
public static void Main(string[] args)
{
CreateWebHostBuilder(args).Build().Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>();
}
IWebHostBuilder remains in 3.0 and is the type of the webBuilder
seen in the preceding code sample. WebHostBuilder will be deprecated in a future release and replaced by HostBuilder
.
The most significant change from WebHostBuilder
to HostBuilder
is in dependency injection (DI). When using HostBuilder
, you can only inject the following into Startup
's constructor:
The HostBuilder
DI constraints:
- Enable the DI container to be built only one time.
- Avoids the resulting object lifetime issues like resolving multiple instances of singletons.
For more information, see Avoiding Startup service injection in ASP.NET Core 3.
AddAuthorization moved to a different assembly
The ASP.NET Core 2.2 and lower AddAuthorization
methods in Microsoft.AspNetCore.Authorization.dll:
- Have been renamed
AddAuthorizationCore
. - Have been moved to Microsoft.AspNetCore.Authorization.Policy.dll.
Apps that are using both Microsoft.AspNetCore.Authorization.dll and Microsoft.AspNetCore.Authorization.Policy.dll aren't impacted.
Apps that are not using Microsoft.AspNetCore.Authorization.Policy.dll should do one of the following:
- Add a reference to Microsoft.AspNetCore.Authorization.Policy.dll. This approach works for most apps and is all that is required.
- Switch to using
AddAuthorizationCore
For more information, see Breaking change in AddAuthorization(o =>
) overload lives in a different assembly #386.
Identity UI
Identity UI updates for ASP.NET Core 3.0:
- Add a package reference to Microsoft.AspNetCore.Identity.UI.
- Apps that don't use Razor Pages must call
MapRazorPages
. See Razor Pages in this document. - Bootstrap 4 is the default UI framework. Set an
IdentityUIFrameworkVersion
project property to change the default. For more information, see this GitHub announcement.
SignalR
The SignalR JavaScript client has changed from @aspnet/signalr
to @microsoft/signalr
. To react to this change, change the references in package.json
files, require
statements, and ECMAScript import
statements.
System.Text.Json is the default protocol
System.Text.Json
is now the default Hub protocol used by both the client and server.
In Startup.ConfigureServices
, call AddJsonProtocol
to set serializer options.
Server:
services.AddSignalR(...)
.AddJsonProtocol(options =>
{
options.PayloadSerializerOptions.WriteIndented = false;
})
Client:
new HubConnectionBuilder()
.WithUrl("/chathub")
.AddJsonProtocol(options =>
{
options.PayloadSerializerOptions.WriteIndented = false;
})
.Build();
Switch to Newtonsoft.Json
If you're using features of Newtonsoft.Json that aren't supported in System.Text.Json, you can switch back to Newtonsoft.Json
. See Use Newtonsoft.Json in an ASP.NET Core 3.0 SignalR project earlier in this article.
Redis distributed caches
The Microsoft.Extensions.Caching.Redis package isn't available for ASP.NET Core 3.0 or later apps. Replace the package reference with Microsoft.Extensions.Caching.StackExchangeRedis. For more information, see Distributed caching in ASP.NET Core.
Opt in to runtime compilation
Prior to ASP.NET Core 3.0, runtime compilation of views was an implicit feature of the framework. Runtime compilation supplements build-time compilation of views. It allows the framework to compile Razor views and pages (.cshtml
files) when the files are modified, without having to rebuild the entire app. This feature supports the scenario of making a quick edit in the IDE and refreshing the browser to view the changes.
In ASP.NET Core 3.0, runtime compilation is an opt-in scenario. Build-time compilation is the only mechanism for view compilation that's enabled by default. The runtime relies on Visual Studio or dotnet-watch in Visual Studio Code to rebuild the project when it detects changes to .cshtml
files. In Visual Studio, changes to .cs
, .cshtml
, or .razor
files in the project being run (Ctrl+F5), but not debugged (F5), trigger recompilation of the project.
To enable runtime compilation in your ASP.NET Core 3.0 project:
Install the Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation NuGet package.
Update
Startup.ConfigureServices
to callAddRazorRuntimeCompilation
:For ASP.NET Core MVC, use the following code:
services.AddControllersWithViews() .AddRazorRuntimeCompilation(...);
For ASP.NET Core Razor Pages, use the following code:
services.AddRazorPages() .AddRazorRuntimeCompilation(...);
The sample at https://github.com/aspnet/samples/tree/main/samples/aspnetcore/mvc/runtimecompilation shows an example of enabling runtime compilation conditionally in Development environments.
For more information on Razor file compilation, see Razor file compilation in ASP.NET Core.
Migrate libraries via multi-targeting
Libraries often need to support multiple versions of ASP.NET Core. Most libraries that were compiled against previous versions of ASP.NET Core should continue working without issues. The following conditions require the app to be cross-compiled:
- The library relies on a feature that has a binary breaking change.
- The library wants to take advantage of new features in ASP.NET Core 3.0.
For example:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netcoreapp3.0;netstandard2.0</TargetFrameworks>
</PropertyGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netcoreapp3.0'">
<FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
<PackageReference Include="Microsoft.AspNetCore" Version="2.1.0" />
</ItemGroup>
</Project>
Use #ifdefs
to enable ASP.NET Core 3.0-specific APIs:
var webRootFileProvider =
#if NETCOREAPP3_0
GetRequiredService<IWebHostEnvironment>().WebRootFileProvider;
#elif NETSTANDARD2_0
GetRequiredService<IHostingEnvironment>().WebRootFileProvider;
#else
#error unknown target framework
#endif
For more information on using ASP.NET Core APIs in a class library, see Use ASP.NET Core APIs in a class library.
Miscellaneous changes
The validation system in .NET Core 3.0 and later treats non-nullable parameters or bound properties as if they had a [Required]
attribute. For more information, see [Required] attribute.
Publish
Delete the bin and obj folders in the project directory.
TestServer
For apps that use TestServer directly with the Generic Host, create the TestServer
on an IWebHostBuilder in ConfigureWebHost:
[Fact]
public async Task GenericCreateAndStartHost_GetTestServer()
{
using var host = await new HostBuilder()
.ConfigureWebHost(webBuilder =>
{
webBuilder
.UseTestServer()
.Configure(app => { });
})
.StartAsync();
var response = await host.GetTestServer().CreateClient().GetAsync("/");
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
}
Breaking API changes
Review breaking changes:
- Complete list of breaking changes in the ASP.NET Core 3.0 release
- Breaking API changes in Antiforgery, CORS, Diagnostics, MVC, and Routing. This list includes breaking changes for compatibility switches.
- For a summary of 2.2-to-3.0 breaking changes across .NET Core, ASP.NET Core, and Entity Framework Core, see Breaking changes for migration from version 2.2 to 3.0.
Endpoint routing with catch-all parameter
Warning
A catch-all parameter may match routes incorrectly due to a bug in routing. Apps impacted by this bug have the following characteristics:
- A catch-all route, for example,
{**slug}"
- The catch-all route fails to match requests it should match.
- Removing other routes makes catch-all route start working.
See GitHub bugs 18677 and 16579 for example cases that hit this bug.
An opt-in fix for this bug is contained in .NET Core 3.1.301 SDK and later. The following code sets an internal switch that fixes this bug:
public static void Main(string[] args)
{
AppContext.SetSwitch("Microsoft.AspNetCore.Routing.UseCorrectCatchAllBehavior",
true);
CreateHostBuilder(args).Build().Run();
}
// Remaining code removed for brevity.
.NET Core 3.0 on Azure App Service
The rollout of .NET Core to Azure App Service is finished. .NET Core 3.0 is available in all Azure App Service datacenters.
ASP.NET Core Module (ANCM)
If the ASP.NET Core Module (ANCM) wasn't a selected component when Visual Studio was installed or if a prior version of the ANCM was installed on the system, download the latest .NET Core Hosting Bundle Installer (direct download) and run the installer. For more information, see Hosting Bundle.