aspnetcore-routing

Qué es y cómo usar el Routing en ASP.NET

  • 5 min

El routing es el mecanismo por el cual un servidor web mapea las solicitudes HTTP a un fragmento específico en el código de nuestra aplicación.

Por ejemplo, si un usuario solicita la URL (ej: GET /products/1), ASP.NET necesita saber qué código debe ejecutar para manejar esta solicitud.

El enrutamiento y es el sistema mediante el cuál definimos que fragmento de código debe ejecutarse, para cada posible URL.

Para ello empleamos el concepto de ruta, que no es más que una cadena de texto que define un patrón que puede coincidir con la URL de una solicitud HTTP.

Las rutas, además, pueden contener segmentos dinámicos que se reemplazarán en función de la solicitud (ahí está la mayor parte de su gracia).

/products/{id}

En este caso, {id} es un parámetro dinámico, y puede coincidir con cualquier valor en esa posición, como por ejemplo /products/123.

Cuando un cliente realiza una solicitud HTTP a una aplicación ASP.NET Core, el sistema de enrutamiento compara la URL de la solicitud con las rutas definidas en la aplicación.

Tipos de enrutamiento

ASP.NET soporta múltiples enfoques de enrutamiento:

Tipo de EnrutamientoDónde se defineVentajas principales
Minimal APIsProgram.cs (desde .NET 6+)✔️ Código conciso
✔️ Rápido para prototipos
Routing por atributosEn controladores)✔️ Flexibilidad
✔️Control explícito
Routing convencionalProgram.cs o Startup.cs✔️ Configuración centralizada
✔️ Útil en apps grandes

Minimal APIs

La opción más sencilla es emplear mappeo de rutas minimalista. definir rutas directamente en Program.cs

Este es un enfoque muy directo, que fue introducido en .NET 6, para tener un comportamiento sencillo y similar al de otros frameworks.

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapGet("/", () => "¡Hola, mundo!");
app.MapGet("/saludo/{nombre}", (string nombre) => $"Hola, {nombre}!");
app.MapPost("/crear", async (HttpContext context) =>
{
    string body = await new StreamReader(context.Request.Body).ReadToEndAsync();
    return Results.Ok($"Datos recibidos: {body}");
});

app.Run();

En este caso:

  • MapGet responde a solicitudes GET en /saludo.
  • MapPost maneja solicitudes POST en /crear.

Enrutado por controladores

El enrutado “convencional” y más usado en ASP.NET es el enrutado a controladores. (un controlador es simplemente un fragmento de código diseñado para responder a una ruta).

Dentro del enrutado por controladores, a su vez podemos tener

  1. Rutas de atributo: Se definen directamente en los controladores y acciones utilizando atributos.
  2. Rutas convencionales: Se definen en el archivo de configuración de la aplicación o mediante atributos en los controladores y acciones.

Es el enrutado más empleado. Las rutas de atributo se definen directamente en los controladores y acciones utilizando el atributo [Route].

Este enfoque es muy flexible y permite definir rutas específicas para cada acción.

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

En este ejemplo:

  • El atributo [Route("api/[controller]")] define una ruta base para el controlador ProductController. La palabra clave [controller] se reemplaza automáticamente por el nombre del controlador (en este caso, Product).
  • El atributo [HttpGet("{id}")] define una ruta específica para la acción GetProduct. La URL /api/product/123 se mapeará a esta acción, y el valor 123 se pasará como parámetro id.

Es una alternativa más antigua de enrutado a controladores, que no emplea atributos.

Se configura en Program.cs o Startup.cs.

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

En este ejemplo:

  • controller: Especifica el controlador que manejará la solicitud.
  • action: Indica la acción o método dentro del controlador.
  • id?: Un parámetro opcional.
  • Una solicitud a https://localhost:5001/Products/Details/5 se mapeará al controlador Products, acción Details, y pasará el parámetro id=5.

Routing convencional se usa menos en nuevos proyectos, pero puedes encontrartelo en aplicaciones existentes.

Combinación de rutas convencionales y de atributo

Es posible combinar rutas convencionales y de atributo en la misma aplicación (aunque probablemente no sea buena idea 🙄) .

En este caso, las rutas de atributo tienen prioridad sobre las rutas convencionales. Esto significa que si una acción tiene una ruta de atributo definida, se utilizará en lugar de la ruta convencional.

En serio, no lo hagáis si podéis evitarlo 🙅

Rutas con parámetros

En cualquiera de los tipos de routing que hemos visto, existe la posibilidad de incluir parámetros que se pasan a las acciones como argumentos. Por ejemplo:

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

En este caso, una URL como /product/123 se mapeará a la acción Details del controlador Product, y el valor 123 se pasará como parámetro id.

Buenas prácticas de diseño

Vamos con algunos consejitos a seguir antes de lanzaros a diseñar vuestras APIs y aplicaciones web.

Nomenclatura consistente

La denominación de los end-points es muy importante al diseñar una API web. Un par de pautas a seguir 👇.

  • Usa sustantivos plurales: /products en lugar de /getProducts
  • Minúsculas y guiones: /user-roles, nunca algo como /userRoles
  • Evita verbos en rutas: Usa los métodos HTTP para indicar acciones

Versionado de APIs

También es muy común que un API cambie en algún momento. Es una buena precaución incluir una referencia a la versión en las APIs.

[ApiController]
[Route("api/v{version:apiVersion}/[controller]")]
public class ProductosController : ControllerBase
{
    [HttpGet("{id}")]
    public IActionResult GetById(int id) { /* ... */ }
}