aspnetcore-routing-por-atributos

What is and how to use attribute routing in ASP.NET

  • 5 min

Attribute routing allows us to specify URL routes directly in controllers and actions using attributes like [Route], [HttpGet], [HttpPost].

It is a very flexible, convenient, and easy-to-maintain approach. So it is the preferred and most commonly used routing method in ASP.NET applications.

Initial setup

Before we can use attribute routing, we must first enable it in the Program.cs file.

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();

var app = builder.Build();

app.MapControllers(); // Enables attribute routing
app.Run();

Main routing attributes

We have two “families” of parameters available for use.

  • The Route attribute
  • The HttpGet, HttpPost… attributes

Let’s look at each of them,

The [Route] attribute is used to define the access route (URL) that must match for a controller or an action to be executed.

It can be used at:

  • Classes (controllers): defines a base route.
  • Methods (actions): defines a specific route for that action.

For example, if we use it in a controller,

[Route("api/products")]
public class ProductsController : ControllerBase
{
    // Here the routes start with /api/products
}

It can be used at the action level, but it is generally used at the controller level, as it does not specify the HTTP verb (get, post, etc…)

These attributes (called “verb attributes”) are used to indicate what type of HTTP request an action handles.

AttributeDescription
[HttpGet]For GET requests
[HttpPost]For POST requests
[HttpPut]For PUT requests
[HttpDelete]For DELETE requests
[HttpPatch]For PATCH requests

For example:

[HttpGet]
public IActionResult Get() { ... }

Complete example

Let’s see how to use each of them with a complete example, where we use them together,

[Route("api/products")]
public class ProductsController : ControllerBase
{
    [HttpGet] // GET /api/products
    public IActionResult GetAll() => Ok();

    [HttpGet("{id}")] // GET /api/products/5
    public IActionResult GetById(int id) => Ok();

    [HttpPost("create")] // POST /api/products/create
    public IActionResult Create([FromBody] Product product) => Ok();

    [HttpDelete("delete/{id}")] // DELETE /api/products/delete/5
    public IActionResult Delete(int id) => Ok();
}

In this example, we see a controller called ProductsController that manages routes under the prefix api/products thanks to the [Route("api/products")] attribute.

  • The action GetAll responds to GET requests at /api/products.
  • GetById is also a GET, but expects an id parameter in the URL, like /api/products/5.
  • Create uses POST with the subroute "create" and receives a Product object in the request body (using [FromBody]).
  • Delete responds to DELETE at the route /api/products/delete/{id}, deleting the corresponding product.

Route parameters

Of course, attribute routing allows us to receive parameters, both in simple routes and complex patterns.

These parameters can be captured directly from the URL and automatically mapped to the arguments of our methods.

Basic parameters

Parameters are defined within curly braces {} and are automatically mapped to method parameters:

[HttpGet("category/{category}/product/{id}")]
public IActionResult GetProductByCategory(string category, int id)
{
    // GET api/products/category/electronics/product/123
    return Ok($"Product {id} in category {category}");
}

Advanced routing patterns

Attribute routes with multiple parameters

Attribute routes can include multiple parameters. For example:

[Route("api/[controller]")]
public class OrderController : ControllerBase
{
    [HttpGet("{orderId}/items/{itemId}")]
    public IActionResult GetOrderItem(int orderId, int itemId)
    {
        // Logic to get the order item
    }
}

In this case, a URL like /api/order/456/items/789 will map to the action GetOrderItem, and the values 456 and 789 will be passed as parameters orderId and itemId, respectively.

Routes with default values

We can define default values for route parameters:

[HttpGet("page/{number:int=1}")]
public IActionResult GetPage(int number)
{
    // GET api/products/page -> number = 1
    // GET api/products/page/5 -> number = 5
    return Ok();
}

In this case, if no value is provided for id, 1 will be used as the default value.

Routes with optional parameters

Optional parameters are indicated with a question mark (?):

[HttpGet("list/{category?}")]
public IActionResult ListProducts(string category = null)
{
    // GET api/products/list
    // GET api/products/list/electronics
    return Ok();
}

If no value is provided for id, the action will return a list of all products.

Multiple routes for one action

[HttpGet("search/{term}")]
[HttpGet("find/{term}")]
public IActionResult SearchProducts(string term)
{
    // Accessible from both routes:
    // GET api/products/search/laptop
    // GET api/products/find/laptop
    return Ok();
}

Route constraints

Route constraints allow limiting the values that parameters can take in a route.

Some of the most common constraints include:

Route patternApplied constraint
{id:int}Only accepts integers
{number:decimal}Only accepts decimal numbers
{price:decimal:range(0.01,999.99)}Decimal in a specific range
{id:int:min(1)}Integer greater than or equal to 1
{code:alpha}Only accepts alphabetic characters
{code:length(5)}String of exactly 5 characters
{name:minlength(3)}String with a minimum of 3 characters
{date:datetime}Only accepts valid dates
{guid:guid}Only accepts valid GUIDs

To apply them, we can specify types and constraints directly in the template:

[Route("api/[controller]")]
public class UserController : ControllerBase
{
    [HttpGet("{id:int}")]
    public IActionResult GetUser(int id)
    {
        // Logic to get the user
    }

    [HttpGet("{name:alpha}")]
    public IActionResult GetUserByName(string name)
    {
        // Logic to get the user by name
    }
}

In this example:

  • The action GetUser will only accept integer values for the parameter id. If a non-integer value is provided, the request will not match this route.
  • The action GetUserByName will only accept alphabetic values for the parameter name. If a non-alphabetic value is provided, the request will not match this route.