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:
MapGet
responds toGET
requests at/greet
.MapPost
handlesPOST
requests 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 theGetProduct
action. The URL/api/product/123
will map to this action, and the value123
will be passed as theid
parameter.
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/5
will map to theProducts
controller,Details
action, 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:
/products
instead 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) { /* ... */ }
}