Skip to content

ASP.NET Core Notes

Attribute Routing AND Conventional Routing

Section titled “Attribute Routing AND Conventional Routing”

ASP.NET Core apps can mix the use of conventional routing and attribute routing. It’s typical to use conventional routes for controllers serving HTML pages for browsers, and attribute routing for controllers serving REST APIs.

Actions are either conventionally routed or attribute routed. Placing a route on the controller or the action makes it attribute routed. Actions that define attribute routes cannot be reached through the conventional routes and vice-versa. Any route attribute on the controller makes all actions in the controller attribute routed.

Attribute routing and conventional routing use the same routing engine.

conventionally-routed controllersattribute-routed controllers
endpoints.MapControllerRoute()YesYes
endpoints.MapAreaControllerRoute()YesYes
endpoints.MapControllers()Yes

If you don’t need conventionally-routed controllers, use endpoints.MapControllers() and routing attributes.

Otherwise use endpoints.MapControllerRoute() and/or endpoints.MapAreaControllerRoute()
for both conventionally-routed controllers and attribute-routed controllers.

endpoints.MapControllers() is not needed for attribute routing
if endpoints.MapControllerRoute() or endpoints.MapAreaControllerRoute() is used.

Sources:

With ASP.NET Core 5.0 or later, cookie names must conform to the token specification requirements.

These characters cannot be in a cookie name:

( ) < > @ , ; : \ " / [ ] ? = { } space tab

So, for example, you cannot create a cookie with the name company:logging:session-id.
However, underscores and dashes are allowed (company_logging_session-id).

Source: Security: Cookie name encoding removed

In ASP.NET MVC and Web API, apps often refer to the current user using the ClaimsPrincipal.Current property. This property isn’t set in ASP.NET Core, and any behavior in your app that depends on it will need to migrate from ClaimsPrincipal.Current by using:

Source: Compare authentication and authorization between ASP.NET MVC and ASP.NET Core

Add this to appsettings.json:

"Logging": {
"LogLevel": {
"Default": "Trace",
"Microsoft.AspNetCore": "Trace",
"Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware": "Trace"
}
},

Add this in HostingExtensions.cs:

app.UseHttpLogging();

Map will update the Request.Path and Request.PathBase to account for branching based on path (trimming the matched path segment off Request.Path and appending it to Request.PathBase), while a seemingly equivalent MapWhen predicate will not. This affects anything downstream that uses the path, such as routing. This is due to the difference between MapMiddleware and MapWhenMiddleware.

Source: Stack Overflow

app.UseExceptionHandler();
app.UseHsts();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseRouting();
app.UseCors();
app.UseAuthentication();
app.UseAuthorization();
app.UseSession();
app.UseMiddleware<CustomMiddleware>();
app.UseEndpoints();

Sources: Understanding Middleware In ASP.NET Core, ASP.NET Core Middleware

AddMvc()AddControllersWithViews()AddController()AddRazorPages()
ControllersYesYesYesYes
Model BindingYesYesYesYes
API ExplorerYesYesYes
AuthorizationYesYesYesYes
CORSYesYesYes
ValidationsYesYesYesYes
Formatter MappingYesYesYes
AntiforgeryYesYesYes
TempDataYesYesYes
ViewsYesYesYes
PagesYesYes
Tag HelpersYesYesYes
Memory CacheYesYesYes

Source: Dot Net Tutorials

Preprocessor Symbols for Target Frameworks

Section titled “Preprocessor Symbols for Target Frameworks”
#if NET8_0_OR_GREATER
void NewMethodNotForFullFramework()
{
}
#endif

Reference: Microsoft Learn - Target Frameworks - Preprocessor Symbols

app.Use(async (context, next) =>
{
var currentEndpoint = context.GetEndpoint();
if (currentEndpoint is null)
{
await next(context);
return;
}
var currentEndpointRouteRequiredValues = "";
if (currentEndpoint is RouteEndpoint routeEndpoint)
{
currentEndpointRouteRequiredValues = string.Join(",", routeEndpoint.RoutePattern.RequiredValues.Select(rv => rv.Key + "=" + rv.Value));
}
Console.WriteLine($"Selected Endpoint: {currentEndpoint.DisplayName} ({currentEndpointRouteRequiredValues})");
var endpointDataSource = app.Services.GetService<EndpointDataSource>();
if (endpointDataSource != null)
{
foreach (var endpoint in endpointDataSource.Endpoints.Cast<RouteEndpoint>())
{
Console.WriteLine($"Endpoint: {endpoint.DisplayName} ({string.Join(",", endpoint.RoutePattern.RequiredValues.Select(rv => rv.Key + "=" + rv.Value).ToArray<string>())})");
}
}
await next(context);
});

or after app.UseEndPoints():

var actionDescriptorCollectionProvider = app.Services.GetService<IActionDescriptorCollectionProvider>();
var firstRoute = actionDescriptorCollectionProvider.ActionDescriptors.Items[8];
var routes = actionDescriptorCollectionProvider.ActionDescriptors.Items
.Where(ad => ad.AttributeRouteInfo != null)
.Select(ad => ad.AttributeRouteInfo.Template)
.ToList();
foreach (var route in routes)
{
Console.WriteLine($"Route Template: {route}");
}