Routing is the mechanism by which a web server maps HTTP requests to a specific fragment in the code of our application.
For example, if a user requests the URL (e.g., GET /products/1), ASP.NET needs to know which code to execute to handle this request.
Routing is the system through which we define which code fragment should be executed for each possible URL.
To do this, we use the concept of route, which is simply a string that defines a pattern that can match the URL of an HTTP request.
Routes can also contain dynamic segments that will be replaced based on the request (that’s where most of the magic is).
/products/{id}In this case, {id} is a dynamic parameter and can match any value in that position, such as /products/123.
When a client makes an HTTP request to an ASP.NET Core application, the routing system compares the request URL with the routes defined in the application.
Types of Routing
ASP.NET supports multiple routing approaches:
| Routing Type | Where Defined | Main Advantages |
|---|---|---|
| Minimal APIs | Program.cs (since .NET 6+) | ✔️ Concise code ✔️ Quick for prototyping |
| Attribute Routing | In controllers) | ✔️ Flexibility ✔️Explicit control |
| Conventional Routing | Program.cs or Startup.cs | ✔️ Centralized configuration ✔️ Useful in large apps |
Minimal APIs
The simplest option is to use minimal route mapping. Define routes directly in Program.cs.
This is a very straightforward approach that was introduced in .NET 6, to have a simple behavior similar to other frameworks.
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", () => "Hello, world!");
app.MapGet("/greet/{name}", (string name) => $"Hello, {name}!");
app.MapPost("/create", async (HttpContext context) =>
{
string body = await new StreamReader(context.Request.Body).ReadToEndAsync();
return Results.Ok($"Data received: {body}");
});
app.Run();In this case:
MapGetresponds toGETrequests at/greet.MapPosthandlesPOSTrequests at/create.
Advantages
✔️ Less boilerplate code ➡️ Ideal for small projects.
Controller Routing
The most commonly used routing in ASP.NET is controller routing. (a controller is simply a fragment of code designed to respond to a route).
Within controller routing, we can have
- Attribute Routes: Defined directly in controllers and actions using attributes.
- Conventional Routes: Defined in the application configuration file or through attributes in controllers and actions.
It is the most employed routing. Attribute Routes are defined directly in controllers and actions using the [Route] attribute.
This approach is very flexible and allows defining specific routes for each action.
[Route("api/[controller]")]
public class ProductController : ControllerBase
{
[HttpGet("{id}")]
public IActionResult GetProduct(int id)
{
// Logic to get the product
}
}In this example:
- The
[Route("api/[controller]")]attribute defines a base route for theProductController. The[controller]keyword is automatically replaced by the controller’s name (in this case,Product). - The
[HttpGet("{id}")]attribute defines a specific route for theGetProductaction. The URL/api/product/123will map to this action, and the value123will be passed as theidparameter.
It is an older alternative to controller routing that does not use attributes.
It is configured in Program.cs or Startup.cs.
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});In this example:
controller: Specifies the controller that will handle the request.action: Indicates the action or method within the controller.id?: An optional parameter.- A request to
https://localhost:5001/Products/Details/5will map to theProductscontroller,Detailsaction, and pass the parameterid=5.
Conventional routing is used less in new projects, but you may encounter it in existing applications.
Combining Conventional and Attribute Routes
It is possible to combine conventional and attribute routes in the same application (though it might not be a good idea 🙄).
In this case, attribute routes take precedence over conventional routes. This means that if an action has an attribute route defined, it will be used instead of the conventional route.
Seriously, don’t do it if you can avoid it 🙅
Routes with Parameters
In any of the routing types we’ve seen, there is the possibility of including parameters that are passed to the actions as arguments. For example:
app.MapControllerRoute(
name: "product",
pattern: "product/{id}",
defaults: new { controller = "Product", action = "Details" });In this case, a URL like /product/123 will map to the Details action of the Product controller, and the value 123 will be passed as the id parameter.
We will see them in more detail in each of the corresponding articles.
Best Design Practices
Let’s go over some tips to follow before you dive into designing your APIs and web applications.
Consistent Naming
Naming your endpoints is very important when designing a web API. A couple of guidelines to follow 👇.
- Use plural nouns:
/productsinstead of/getProducts - Lowercase and hyphens:
/user-roles, never something like/userRoles - Avoid verbs in routes: Use HTTP methods to indicate actions
API Versioning
It is also very common for an API to change at some point. It’s a good precaution to include a version reference in APIs.
[ApiController]
[Route("api/v{version:apiVersion}/[controller]")]
public class ProductsController : ControllerBase
{
[HttpGet("{id}")]
public IActionResult GetById(int id) { /* ... */ }
}