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.
Atributo | Descripció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ámetroid
en la URL, como/api/productos/5
.Crear
usa POST con la subruta"crear"
y recibe un objetoProducto
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 ruta | Restricció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ámetroid
. 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ámetroname
. Si se proporciona un valor no alfabético, la solicitud no coincidirá con esta ruta.