aspnetcore-routing

What is Routing and How to Use it in ASP.NET

  • 5 min

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 TypeWhere DefinedMain Advantages
Minimal APIsProgram.cs (since .NET 6+)✔️ Concise code
✔️ Quick for prototyping
Attribute RoutingIn controllers)✔️ Flexibility
✔️Explicit control
Conventional RoutingProgram.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 to GET requests at /greet.
  • MapPost handles POST requests at /create.

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

  1. Attribute Routes: Defined directly in controllers and actions using attributes.
  2. 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 the ProductController. The [controller] keyword is automatically replaced by the controller’s name (in this case, Product).
  • The [HttpGet("{id}")] attribute defines a specific route for the GetProduct action. The URL /api/product/123 will map to this action, and the value 123 will be passed as the id 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 the Products controller, Details action, and pass the parameter id=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.

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) { /* ... */ }
}