22 KiB
title | author | description | ms.author | ms.custom | ms.date | uid |
---|---|---|---|---|---|---|
Migrate from ASP.NET Core 2.2 to 3.0 Preview | rick-anderson | Learn how to migrate an ASP.NET Core 2.2 project to ASP.NET Core 3.0. | riande | mvc | 09/03/2019 | migration/22-to-30 |
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.
Prerequisites
Visual Studio
Visual Studio Code
Visual Studio for Mac
Update the project file
-
Set the Target Framework Moniker (TFM) to
netcoreapp3.0
:<TargetFramework>netcoreapp3.0</TargetFramework>
-
Remove any
<PackageReference>
to the Microsoft.AspNetCore.All or Microsoft.AspNetCore.App metapackage. -
Remove any
<PackageReference>
to the Microsoft.AspNetCore.Razor.Design package. -
If your application uses API analyzers, remove any
<PackageReference>
element for the Microsoft.AspNetCore.Mvc.Api.Analyzers package. Edit your project file to use the analyzer shipped as part of the .NET Core SDK:
<PropertyGroup>
<IncludeOpenAPIAnalyzers>true</IncludeOpenAPIAnalyzers>
</PropertyGroup>
-
Update the
Version
attribute on remaining<PackageReference>
elements forMicrosoft.AspNetCore.*
packages to the current preview (for example,3.0.0-preview5-19227-01
).If there's no 3.0 version of a package, the package might have been deprecated in 3.0. Many of these packages are part of
Microsoft.AspNetCore.App
and shouldn't be referenced individually. For a preliminary list of packages no longer produced in 3.0, see Stop producing packages for shared framework assemblies in 3.0 (aspnet/AspNetCore #3756). The shared framework is the set of assemblies (.dll files) that are installed on the machine and referenced byMicrosoft.AspNetCore.App
. For more information, see The shared framework. -
The assemblies for several notable components were removed from
Microsoft.AspNetCore.App
in 3.0. Add<PackageReference>
elements if you're using APIs from packages listed in Assemblies being removed from Microsoft.AspNetCore.App 3.0 (aspnet/AspNetCore #3755).Examples of removed components include:
Microsoft.AspNet.WebApi.Client
Microsoft.EntityFrameworkCore
System.Data.SqlClient
The list of assemblies shipping in
Microsoft.AspNetCore.App
hasn't been finalized and will change before 3.0 RTM.Consider the following code:
var branches = await response.Content.ReadAsAsync<IEnumerable<GitHubBranch>>();
The
ReadAsAsync
method called in the preceding code is included inMicrosoft.AspNet.WebApi.Client
. Install the Microsoft.AspNet.WebApi.Client NuGet package to resolve the compilation issue in 3.0. -
Add Json.NET support.
-
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 isInProcess
.
Json.NET support
As part of the work to improve the ASP.NET Core shared framework, Json.NET has been removed from the ASP.NET Core shared framework.
To use Json.NET in an ASP.NET Core 3.0 project:
-
Add a package reference to Microsoft.AspNetCore.Mvc.NewtonsoftJson.
-
Update
Startup.ConfigureServices
to callAddNewtonsoftJson
.services.AddMvc() .AddNewtonsoftJson();
AddNewtonsoftJson
is compatible with the new MVC service registration methods:AddRazorPages
AddControllersWithViews
AddControllers
services.AddControllers() .AddNewtonsoftJson();
Json.NET settings can be set in the call to
AddNewtonsoftJson
:services.AddMvc() .AddNewtonsoftJson(options => options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver());
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 UseMvc
. 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.AddControllers();
services.AddRazorPages();
}
Update 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
, you may want to avoid migrating at this time. Provide feedback at Plan to migrate IRouter based implementations onto Endpoint Routing. -
If the app directly accesses
RouteData.Routers
inside MVC, you may want to avoid migrating at this time. Provide feedback at Migration guidance for using RouteData.Routers.
Endpoint Routing supports the same route pattern syntax and route pattern authoring features as IRouter
. Endpoint Routing supports IRouteContraint
. 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
afterUseRouting
(and afterUseCors
if CORS Middleware is used). - Replace
UseMvc
orUseSignalR
withUseEndpoints
. - If the app uses CORS scenarios, such as
[EnableCors]
, place the call toUseCors
before any other middlewares that use CORS (for example, placeUseCors
beforeUseAuthentication
,UseAuthorization
, andUseMvc
). - Replace
IHostingEnvironment
withIWebHostEnvironment
and add ausing
statement for theMicrosoft.Extensions.Hosting
namespace.
The following 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?}");
});
}
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 : ControllerBase
{
[Authorize]
public IActionResult BuyWidgets()
{
...
}
}
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 : ControllerBase
{
...
}
Policies can also be customized. Building upon the previous example, the DefaultPolicy
is configured to require authentication and a specific scope:
public void ConfigureServices(IServiceCollection services)
{
...
services.AddAuthorization(options =>
{
options.DefaultPolicy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.RequireScope("MyScope")
.Build();
});
}
public void Configure(IApplicationBuilder app)
{
...
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapDefaultControllerRoute().RequireAuthorization();
});
}
[AllowAnonymous]
public class HomeController : ControllerBase
{
...
}
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()
.RequireScope("MyScope")
.Build();
});
}
public void Configure(IApplicationBuilder app)
{
...
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapDefaultControllerRoute();
});
}
[AllowAnonymous]
public class HomeController : ControllerBase
{
...
}
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. UseEndpoint
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.
Migrate 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.
Migrate 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?}");
});
}
Migrate 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 example, 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);
Migrate 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:
The following code shows the ASP.NET Core 2.2 template-generated Program
class:
xref:Microsoft.AspNetCore.Hosting.IWebHostBuilder remains in 3.0 and is the type of the webBuilder
seen in the preceding code sample. xref:Microsoft.AspNetCore.Hosting.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 xref:Microsoft.Extensions.Configuration.IConfiguration and xref:Microsoft.AspNetCore.Hosting.IHostingEnvironment 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.
Update SignalR code
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.WriteIndented = false;
})
Client:
new HubConnectionBuilder()
.WithUrl("/chatHub")
.AddJsonProtocol(options =>
{
options.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
:
-
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(...);
Opt in to runtime compilation
In 3.0, runtime compilation is an opt-in scenario. To enable runtime compilation, see https://docs.microsoft.com/aspnet/core/mvc/views/view-compilation?view=aspnetcore-3.0#runtime-compilation.