Routing in Asp.Net Core 3.0
Routing is a pattern matching system which maps incoming request to appropriate controller and action methods.
There are two types of routing in ASP.Net Core
- Conventional Based routing
- Attribute-based routing
Routing in ASP.NET Core 3.0 is a bit different than older version if you see Configure method of ASP.NET Core 2.2 you will see routing we use to implement using an app.UseMVC method and if we want to use other services like (MVC, Razor Pages, SignalR, etc.), then we need to configure it in the Configure method.

Now the problem with routing was the other middleware in the pipeline did not know the request which is sent is going to be handled by which middleware, to overcome this in ASP.NET CORE introduce UseEndpointRouting ‘Endpoint routing middleware‘ middleware and UseEndpoints ‘Endpoint middleware‘.

Endpoint routing middleware uses metadata and also checks request URL and determine which endpoint will best to handle request then selected endpoint is set in the HttpContext object from here other middleware can access the chosen Endpoint after that UseEndpoints ‘Endpoint middleware‘ will execute the endpoint which chose.
After understanding how new routing works in asp.net core 3.0 next let’s create a simple application and using both Conventional Based routing and Attribute-based routing.

We have created an ASP.NET Core project for implementing Convention Routing and Attribute-based routing.
The Convention Routing is a default routing it is present when you create a new application. It is called convention routing because it establishes a convention for URL paths.
- the first path segment maps to the controller name
- the second maps to the action name.
- the third segment is used for an optional id used to map to a model entity
Reference:- https://docs.microsoft.com/en-us/aspnet/core/mvc/controllers/routing?view=aspnetcore-3.1
Convention Routing which is available by default
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
}
Details of each parameter
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}",
constraints: null,
dataTokens: null,
defaults: new {controller = "Home", action = "Validate"});
name (String)
The name of the route.
pattern (String)
The URL pattern of the route.
Defaults (Object)
An object that contains default values for route parameters. The object’s properties represent the names and values of the default values.
Constraints (Object)
An object that contains constraints for the route. The object’s properties represent the names and values of the constraints.
dataTokens (Object)
An object that contains data tokens for the route. The object’s properties represent the names and values of the data tokens.
Referenced from:- https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.builder.controllerendpointroutebuilderextensions.mapcontrollerroute?view=aspnetcore-3.1
Now let’s add a controller with name ProductController and it will have an action method with name ProductDetails which will take id as an input parameter.
Code Snippet
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
namespace WebRouting.Controllers
{
public class ProductController : Controller
{
public IActionResult ProductDetails(int? id)
{
return View();
}
}
}
Now we are going to add Route for handling this request. Here we have added a Route with the name ‘ProductRoute‘ and it has URL pattern for ProductRoute “Product/{id}” which tells that any URL that starts with /Product, must be handled by ProductController.
If you see ProductRoute in below code snippet, we haven’t specified {action} in the URL pattern because we want every URL that starts with Product should always use ProductDetails action of ProductController.
Code Snippet
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "ProductRoute",
pattern: "Product/{id:int}",
defaults: new { controller = "Product", action = "ProductDetails" });
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}",
constraints: null,
dataTokens: null,
defaults: new { controller = "Home", action = "Index" });
});
Next, we are going to register various route and see which kind of pattern is handled by which URL.
First scenario
In this scenario, we have route prefix ‘Search‘ and placeholder for the category which we are going to pass as a parameter to ProductDetailsbyCategory action method in Product Controller.
endpoints.MapControllerRoute(
name: "ProductCategoryRoute",
pattern: "Search/{category}",
defaults: new { controller = "Product", action = "ProductDetailsbyCategory" });
URL | Controller | Action | Id | Parameter 1 | Parameter 2 |
http://localhost:44341/search/mobile | Product | ProductDetailsbyCategory | – | mobile | – |
Second scenario
In this scenario, we have route prefix ‘Search‘ and two placeholders for the category and another for subCategory which we are going pass as a parameter to ProductDetailsbyCategoryandsubCategory action method in Product Controller.
endpoints.MapControllerRoute(
name: "ProductCategoryandsubCategoryRoute",
pattern: "Search/{category}/{subCategory}",
defaults: new { controller = "Product", action = "ProductDetailsbyCategoryandsubCategory" });
URL | Controller | Action | Id | Parameter 1 | Parameter 2 |
https://localhost:44341/search/mobile/motorola | Product | ProductDetailsbyCategoryandsubCategory | – | mobile | motorola |
Third scenario
In this scenario, we have route prefix ‘Product‘ and one placeholder for productid which we are going pass as a parameter to ProductDetails action method in Product Controller.
endpoints.MapControllerRoute(
name: "ProductRoute",
pattern: "Product/{id:int}",
defaults: new { controller = "Product", action = "ProductDetails" });
URL | Controller | Action | Id | Parameter 1 | Parameter 2 |
https://localhost:44341/product/1 | Product | ProductDetails | 1 | – | – |
Fourth scenario
The default route which is present when we create new ASP.NET Core Application.
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}",
defaults: new { controller = "Home", action = "Index" });
URL | Controller | Action | Id | Parameter 1 | Parameter 2 |
https://localhost:44341/product/1 | Product | ProductDetails | 1 | – | – |
Attribute-based routing
In attribute routing, we use an attribute to define routes using attribute routing give you more control on routing compare to conventional routing. In attribute routing, we directly apply Route attribute on the controller and action method.
Lists the constraints that are supported
Constraint | Description | Example |
alpha | Matches uppercase or lowercase Latin alphabet characters (a-z, A-Z) | {x:alpha} |
bool | Matches a Boolean value. | {x:bool} |
datetime | Matches a DateTime value. | {x:datetime} |
decimal | Matches a decimal value. | {x:decimal} |
double | Matches a 64-bit floating-point value. | {x:double} |
float | Matches a 32-bit floating-point value. | {x:float} |
guid | Matches a GUID value. | {x:guid} |
int | Matches a 32-bit integer value. | {x:int} |
length | Matches a string with the specified length or within a specified range of lengths. | {x:length(6)} {x:length(1,20)} |
long | Matches a 64-bit integer value. | {x:long} |
max | Matches an integer with a maximum value. | {x:max(10)} |
maxlength | Matches a string with a maximum length. | {x:maxlength(10)} |
min | Matches an integer with a minimum value. | {x:min(10)} |
minlength | Matches a string with a minimum length. | {x:minlength(10)} |
range | Matches an integer within a range of values. | {x:range(10,50)} |
regex | Matches a regular expression. | {x:regex(^\d{3}-\d{3}-\d{4}$)} |
Reference from:- https://docs.microsoft.com
We do not require any changes in Configure method we need to see we have endpoints.MapControllerRoute method in UseEndpoints method which Adds endpoints for controller actions to the IEndpointRouteBuilder.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
}
Here we are going to use the Same ProductController which we have used in Conventional routing which will help us to understand attribute routing easily.
In below code, I have added Route attribute to each action method with, without parameters and with routing constraints.
Code snippet
using Microsoft.AspNetCore.Mvc;
namespace WebRouting.Controllers
{
public class ProductController : Controller
{
[Route("/Product/ProductDetails/{id:int}")]
public IActionResult ProductDetails(int? id)
{
return View();
}
[Route("Search/{category}")]
public IActionResult ProductDetailsbyCategory(string category= "")
{
return View();
}
[Route("Search/{category}/{subCategory}")]
public IActionResult ProductDetailsbyCategoryandsubCategory(string category = "", string subCategory = "")
{
return View();
}
[Route("MyCart")]
public IActionResult Cart()
{
return View();
}
}
}
After adding all route for a sample next let’s see how we can access then each in simple steps.
Route with Name ( eg: /mycart )
To access the below action method, we are going to enter URL https://localhost:44341/mycart
[Route("MyCart")]
public IActionResult Cart()
{
return View();
}
Route with integer parameter and integer route constraint (eg: /Product/ProductDetails/1)
To access the below action method, we are going to enter URL https://localhost:44341/Product/ProductDetails/1
[Route("/Product/ProductDetails/{id:int}")]
public IActionResult ProductDetails(int? id)
{
return View();
}
Route with one string parameter ‘category’ ( eg: /search/mobile)
To access the below action method, we are going to enter URL https://localhost:44341/search/mobile
[Route("Search/{category}")]
public IActionResult ProductDetailsbyCategory(string category= "")
{
return View();
}
Route with two string parameter ‘category’ and ‘subCategory’ ( eg: /search/mobile/motorola)
To access the below action method, we are going to enter URL:- https://localhost:44341/search/mobile/motorola
[Route("Search/{category}/{subCategory}")]
public IActionResult ProductDetailsbyCategoryandsubCategory(string category = "",
string subCategory = "")
{
return View();
}