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 our application’s code.

For example, if a user requests the URL (e.g., GET /products/1), ASP.NET needs to know what code should be executed 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 a route, which is simply a text string that defines a pattern that can match the URL of an HTTP request.

Routes can also contain dynamic segments that are replaced based on the request (that’s where most of their power lies).

/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 it’s definedMain Advantages
Minimal APIsProgram.cs (from .NET 6+)✔️ Concise code
✔️ Fast for prototypes
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 minimalist route mapping, defining routes directly in Program.cs.

This is a very straightforward approach, introduced in .NET 6, to have 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();
Copied!

In this case:

  • MapGet responds to GET requests at /greet.
  • MapPost handles POST requests at /create.

Controller Routing

The “conventional” and most used routing in ASP.NET is controller routing. (a controller is simply a code fragment designed to respond to a route).

Within controller routing, we can in turn have:

  1. Attribute routes: Defined directly on controllers and actions using attributes.
  2. Conventional routes: Defined in the application’s configuration file or via attributes on controllers and actions.

This is the most commonly used routing. Attribute routes are defined directly on 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
    }
}
Copied!

In this example:

  • The [Route("api/[controller]")] attribute defines a base route for the ProductController. The [controller] keyword is automatically replaced by the controller name (in this case, Product).
  • The [HttpGet("{id}")] attribute defines a specific route for the GetProduct action. The URL /api/product/123 will be mapped to this action, and the value 123 will be passed as the id parameter.

This is an older alternative for controller routing that doesn’t use attributes.

It’s configured in Program.cs or Startup.cs.

app.UseEndpoints(endpoints =>
{
   endpoints.MapControllerRoute(
	   name: "default",
	   pattern: "{controller=Home}/{action=Index}/{id?}");
});
Copied!

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 be mapped to the Products controller, Details action, and will pass the parameter id=5.

Conventional routing is used less in new projects, but you might encounter it in existing applications.

Combining Conventional and Attribute Routes

It’s possible to combine conventional and attribute routes in the same application (although it’s probably not 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, it’s possible to include parameters that are passed to actions as arguments. For example:

app.MapControllerRoute(
    name: "product",
    pattern: "product/{id}",
    defaults: new { controller = "Product", action = "Details" });
Copied!

In this case, a URL like /product/123 will be mapped to the Details action of the Product controller, and the value 123 will be passed as the id parameter.

Design Best Practices

Let’s go over some tips to follow before you dive into designing your APIs and web applications.

Consistent Naming

The naming of 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’s 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) { /* ... */ }
}
Copied!