The Changes Introduced by Microsoft for ASP.NET Core from .NET 5

Posted by

Microsoft released the .NET 5 platform in November, focusing a massive investment primarily to improve the platform’s overall performance. Besides, they have also included a broad set of new features in ASP.NET Core. These advanced features are related principally to Web API, SignalR, and Blazon. Moreover, ASP.NET MVC includes extra support for more model binding types and a progressive library. Microsoft.Web.Identity simplifies the authentication integration of Azure Active Directory.

hire developers to witness the significant changes in .NET 5. Take a glance at how the system works:


Numbers appearing as quoted strings can now be deserialized into numeric properties as if they were regular JSON numbers. Previously this would have thrown a JsonException.

Changed Packages

Packages are renamed and changed; take a look at the lists of inclusion in the new version: 

  • AzureAD.UI => Microsoft.Identity.Web
  • AzureADB2C.UI APIs => Microsoft.Identity.Web
  • Microsoft.AspNetCore.DataProtection.AzureKeyVault => Azure.Extensions.AspNetCore.DataProtection.Keys
  • Microsoft.AspNetCore.DataProtection.AzureStorage => Azure.Extensions.AspNetCore.DataProtection.Blobs
  • Microsoft.Extensions.Configuration.AzureKeyVault => Azure.Extensions.AspNetCore.Configuration.Secrets

Blazor Changes for Performance

For library authors supporting both .NET Core 3.x and .NET 5, you need to use a conditional Microsoft.AspNetCore.Microsoft provides this sample:

<PackageReference Include=”Microsoft.AspNetCore.Components” Version=”3.0.0″ Condition=”‘$(TargetFramework)’ == ‘netstandard2.0′” />

<PackageReference Include=”Microsoft.AspNetCore.Components” Version=”5.0.0-rc.1.*” Condition=”‘$(TargetFramework)’ != ‘netstandard2.0′” />

Blazor Drops Support for IE and Edge Legacy

Blazor in .NET 5 requires additional functionality that isn’t available in older browsers as it does not support internet explorer at all. Previously, IE 11 could access Blazor-based websites via polyfills and asm.js. But according to Daniel Roth, it had “lots of issues and the experience wasn’t great” so Microsoft dropped the asm.js route and focused on WebAssembly.

Edge Legacy is likewise not supported by Blazor with .NET 5. They haven’t given any specific reason. Still, it was deemed unnecessary because this browser will no longer be supported as of March 9, 2021. Edge Legacy was officially replaced by a Chromium-based version of Edge in January 2020.

HttpClient Logging

Sometimes minor changes can have significant impacts on a project. One such case is how HttpClient instances were created by IHttpClientFactory log integer status codes. In .NET Core, developers wrote logs with HTTP status code names. For example:

Received HTTP response after 56.0044ms – OK

 End processing HTTP request after 70.0862ms – OK.

To be more consistent with other parts of .NET, they change to use HTTP status code integers.

Received HTTP response after 56.0044ms – 200

 End processing HTTP request after 70.0862ms – 200 the purpose of the changing aspects is to finalize more advanced suitability and performance and ensuring better scalability in the future. 

BadHttpRequestException Was Replaced with BadHttpRequestException

This odd sounding change is the result of .NET having three BadHttpRequestException classes. Their full names are Microsoft.AspNetCore.Server.Kestrel.BadHttpRequestException, Microsoft.AspNetCore.Server.IIS.BadHttpRequestException, and Microsoft.AspNetCore.Http.BadHttpRequestException. 

Kestrel: Default TLS Protocol Versions Change

Instead of having a hard-coded list of supported TLS protocols, Kestrel will now defer to the operating system. This means if a flaw in an existing protocol is found, they disable it through an OS setting or patch without changing the application. For a list of currently supported protocols on Windows, see Ensuring support for TLS 1.2 across deployed operating systems.

Pubternal APIs Removed

If the term “pubternal” is unfamiliar to you, you’re not alone. This unusual word refers to APIs marked as public but is in a namespace that implies they were meant to be internal to the library. A couple of examples of this in ASP.NET Core are:

  • Microsoft.Extensions.Localization.Internal.AssemblyWrapper
  • Microsoft.Extensions.Localization.Internal.IResourceStringProvider

These classes were initially marked as public, so Microsoft could use them as “extension points for the team’s internal testing”. As .NET already provides better ways to handle this, such as the InternalsVisibleTo attribute, this should not have been done. So, to avoid future problems with non-Microsoft developers using these classes, they have been marked as internal.

MVC: Objectmodelvalidator Calls a New Overload of Validationvisitor.Validate

Usually, adding new methods to a class does not result in a breaking change. But in the case of ValidationVisitor.Validate, things are a bit more complicated.

The original signature for this method was: public virtual bool Validate(ModelMetadata metadata, string key, object model, bool alwaysValidateAtTopLevel)

As a virtual method, developers can overload it to perform custom validation logic. MVC’s ObjectModelValidator would then invoke this logic.

In .NET 5, a new virtual overload was introduced, and the original was redirected to call it. Public virtual bool Validate(ModelMetadata metadata, string key, object model, bool alwaysValidateAtTopLevel)

=> Validate(metadata, key, model, alwaysValidateAtTopLevel, container: null);

Public virtual bool Validate(ModelMetadata metadata, string key, object model, bool alwaysValidateAtTopLevel, object container)

If these were not virtual methods, there wouldn’t have been a problem. Developers simply wouldn’t have known the new overload was being called. Unfortunately, that’s not the case here.

If a developer overrides the original Validate method, then in .NET 5, that override would be ignored. The code would still compile, but the ObjectModelValidator will call the new overload instead, and things would simply stop working correctly without any explanation.

Cookie Name Encoding Replaced

In the HTTP standard, cookie names were a limited subset of ASCII characters. As a convenience to developers, many frameworks, including ASP.NET Core would allow additional characters to be included and would simply encode them. This capability has been removed due to the risk it poses.

An issue was discovered in multiple web frameworks where this encoding/decoding could allow an attacker to bypass a security feature called cookie prefixes by spoofing the reserved prefixes like __Host- with encoded values like __%48ost-. This attack requires a secondary exploit to inject the spoofed cookies, such as an XSS vulnerability in the web site. These prefixes are not used by default in ASP.NET Core or Microsoft. Owin libraries or templates.

SignalR: MessagePack Hub Protocol Now Uses MessagePackSerializerOptions

Previously, SignalR used the MessagePack 1 to handle message serialization. With .NET 5, it was updated to use MessagePack 2. Since MessagePack 2 had a breaking change to switch from IFormatterResolver to MessagePackSerializerOptions, SignalR needed to make a similar change in how it handles its configuration.

This change only affects those who modify the MessagePackHubProtocolOptions for SignalR.

SignalR Now Requires Endpoint Routing

Endpoint routing, as we now know, has only been around since ASP.NET Core 3. In his article titled Understanding ASP.NET Core Endpoint Routing, Areg Sarkissian why it was introduced:

Prior to endpoint routing, the routing resolution for an ASP.NET Core application was made in the ASP.NET Core MVC middleware at the end of the HTTP request processing pipeline. This meant that route information, such as what controller action would be executed, was not available to middleware that processed the request before the MVC middleware in the middleware pipeline.

It is advantageous to have this route information available, for example, in a CORS or authorization middleware, to use it as a factor in the authorization process.

Fortunately, the physical change to the code is minor:

app.UseSignalR(routes =>   





app.UseEndpoints(endpoints =>