aspnetcore-routing-por-atributos

Qué es y cómo usar el enrutamiento por atributos en ASP.NET

  • 5 min

El enrutamiento por atributos nos permite especificar las rutas URL directamente en los controladores y acciones usando atributos como [Route], [HttpGet], [HttpPost].

Es un enfoque muy flexible, cómodo y fácil de mantener. Así que es el método de routing preferido y más empleado en aplicaciones de ASP.NET.

Configuración inicial

Antes de poder usar el routing por atributos, primero debemos tenerlo habilitado en el archivo Program.cs.

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();

var app = builder.Build();

app.MapControllers(); // Habilita el enrutamiento por atributos
app.Run();

Atributos de enrutamiento principales

Tenemos dos “familias” de parámetros disponibles para usar.

  • El atributo Route
  • Los atributos HttpGet, HttpPost

Vamos a ver cada uno de ellos,

El atributo [Route] se utiliza para definir la ruta de acceso (URL) que debe coincidir para que se ejecute un controlador o una acción.

Se puede usar en:

  • Clases (controladores): define una ruta base.
  • Métodos (acciones): define una ruta específica para esa acción.

Por ejemplo, su lo usamos en un controlador,

[Route("api/productos")]
public class ProductosController : ControllerBase
{
    // Aquí las rutas empiezan con /api/productos
}

Puede usarse a nivel de acción, pero generalmente se usará en el controller, porque no especifica el verbo de HTTP (get, post, etc…)

Estos atributos (llamados “atributos de verbo”) se usan para indicar qué tipo de solicitud HTTP maneja una acción.

AtributoDescripción
[HttpGet]Para solicitudes GET
[HttpPost]Para solicitudes POST
[HttpPut]Para solicitudes PUT
[HttpDelete]Para solicitudes DELETE
[HttpPatch]Para solicitudes PATCH

Por ejemplo:

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

Ejemplo completo

Vamos a ver como usa cada uno de ellos con un ejemplo completo, donde los usemos de forma conjunta,

[Route("api/productos")]
public class ProductosController : ControllerBase
{
    [HttpGet] // GET /api/productos
    public IActionResult ObtenerTodos() => Ok();

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

    [HttpPost("crear")] // POST /api/productos/crear
    public IActionResult Crear([FromBody] Producto producto) => Ok();

    [HttpDelete("borrar/{id}")] // DELETE /api/productos/borrar/5
    public IActionResult Borrar(int id) => Ok();
}

En este ejemplo vemos un controlador llamado ProductosController que gestiona rutas bajo el prefijo api/productos gracias al atributo [Route("api/productos")].

  • La acción ObtenerTodos responde a solicitudes GET en /api/productos.
  • ObtenerPorId también es un GET, pero espera un parámetro id en la URL, como /api/productos/5.
  • Crear usa POST con la subruta "crear" y recibe un objeto Producto en el cuerpo de la solicitud (usando [FromBody]).
  • Borrar responde a DELETE en la ruta /api/productos/borrar/{id}, eliminando el producto correspondiente.

Parámetros de ruta

Por supuesto, el enrutamiento por atributos nos permite recibir parámetros, tanto en rutas simples como en patrones complejos.

Estos parámetros pueden capturarse directamente desde la URL y automáticamente mapearse a los argumentos de nuestros métodos.

Parámetros básicos

Los parámetros se definen entre llaves {} y se mapean automáticamente a los parámetros del método:

[HttpGet("categoria/{categoria}/producto/{id}")]
public IActionResult GetProductoPorCategoria(string categoria, int id)
{
    // GET api/productos/categoria/electronicos/producto/123
    return Ok($"Producto {id} en categoría {categoria}");
}

Patrones avanzados de rutas

Rutas de atributo con múltiples parámetros

Las rutas de atributo pueden incluir múltiples parámetros. Por ejemplo:

[Route("api/[controller]")]
public class OrderController : ControllerBase
{
    [HttpGet("{orderId}/items/{itemId}")]
    public IActionResult GetOrderItem(int orderId, int itemId)
    {
        // Lógica para obtener el ítem del pedido
    }
}

En este caso, una URL como /api/order/456/items/789 se mapeará a la acción GetOrderItem, y los valores 456 y 789 se pasarán como parámetros orderId e itemId, respectivamente.

Rutas con valores predeterminados

Podemos definir valores predeterminados para los parámetros de la ruta:

[HttpGet("pagina/{numero:int=1}")]
public IActionResult ObtenerPagina(int numero)
{
    // GET api/productos/pagina -> numero = 1
    // GET api/productos/pagina/5 -> numero = 5
    return Ok();
}

En este caso, si no se proporciona un valor para id, se usará 1 como valor predeterminado.

Rutas con parámetros opcionales

Los parámetros opcionales se indican con un signo de interrogación (?):

[HttpGet("lista/{categoria?}")]
public IActionResult ListarProductos(string categoria = null)
{
    // GET api/productos/lista
    // GET api/productos/lista/electronicos
    return Ok();
}

Si no se proporciona un valor para id, la acción devolverá una lista de todos los productos.

Rutas múltiples para una acción

[HttpGet("buscar/{termino}")]
[HttpGet("search/{termino}")]
public IActionResult BuscarProductos(string termino)
{
    // Accesible desde ambas rutas:
    // GET api/productos/buscar/laptop
    // GET api/productos/search/laptop
    return Ok();
}

Restricciones de ruta

Las restricciones de ruta permiten limitar los valores que pueden tomar los parámetros en una ruta.

Algunas de las restricciones más comunes incluyen:

Patrón de rutaRestricción aplicada
{id:int}Solo acepta enteros
{numero:decimal}Solo acepta números decimales
{precio:decimal:range(0.01,999.99)}Decimal en rango específico
{id:int:min(1)}Entero mayor o igual a 1
{codigo:alpha}Solo acepta caracteres alfabéticos
{codigo:length(5)}String de exactamente 5 caracteres
{nombre:minlength(3)}String con mínimo 3 caracteres
{fecha:datetime}Solo acepta fechas válidas
{guid:guid}Solo acepta GUIDs válidos

Para aplicarlas, podemos especificar tipos y restricciones directamente en el template:

[Route("api/[controller]")]
public class UserController : ControllerBase
{
    [HttpGet("{id:int}")]
    public IActionResult GetUser(int id)
    {
        // Lógica para obtener el usuario
    }

    [HttpGet("{name:alpha}")]
    public IActionResult GetUserByName(string name)
    {
        // Lógica para obtener el usuario por nombre
    }
}

En este ejemplo:

  • La acción GetUser solo aceptará valores enteros para el parámetro id. Si se proporciona un valor no entero, la solicitud no coincidirá con esta ruta.
  • La acción GetUserByName solo aceptará valores alfabéticos para el parámetro name. Si se proporciona un valor no alfabético, la solicitud no coincidirá con esta ruta.