Ya sabemos usar los middleware que Microsoft nos da (UseCors, UseStaticFiles…). Pero la verdadera potencia de ASP.NET Core reside en que nosotros podemos fabricar nuestros propios middleware.
Por ejemplo ¿Necesitas loguear cada petición que tarda más de 1 segundo? ¿Quieres bloquear peticiones que vengan de un país concreto? ¿Necesitas inyectar una cabecera HTTP personalizada en todas las respuestas?
Para todo eso, necesitas crear un Custom Middleware. Vamos a ver como crear nuestros propios middleware.
La anatomía de la interceptación
Recordemos que el pipeline es una serie de componentes conectados en cadena que procesan las peticiones HTTP. Nuestro middleware va a estar “en medio”.
El patrón fundamental para escribir un middleware es este:
- Hacer algo antes (Manipular la Request).
- Llamar al siguiente (await next()).
- Hacer algo después (Manipular la Response).
Forma 1: Middleware “Inline”
Si la lógica es muy sencilla, podemos definir el middleware directamente en el Program.cs usando una función lambda.
var app = builder.Build();
app.Use(async (context, next) =>
{
// 1. Lógica de ENTRADA
Console.WriteLine($"--> Recibida petición a: {context.Request.Path}");
// 2. Pasar el testigo al siguiente middleware
await next();
// 3. Lógica de SALIDA
Console.WriteLine($"<-- Respuesta enviada con código: {context.Response.StatusCode}");
});
app.MapGet("/", () => "Hola Middleware");
app.Run();
Fíjate en el await next(). Es la línea más importante.
- Todo lo que escribas antes, ocurre cuando la petición baja.
- Todo lo que escribas después, ocurre cuando la respuesta sube.
¡No olvides el await next()! Si se te olvida llamar a next(), el pipeline se corta (cortocircuito). La petición nunca llegará a tu controlador y el usuario recibirá una página en blanco o un error 404.
Forma 2: Clase Middleware
Llenar el Program.cs de lambdas es sucio. Lo correcto es encapsular esta lógica en una clase dedicada.
Vamos a crear nuestro ResponseTimeMiddleware. Su misión será cronometrar cuánto tarda nuestra API en responder.
Paso 1: Crear la clase
Un middleware en .NET debe cumplir dos reglas por convención (no por interfaz obligatoria, curiosamente):
- Tener un constructor que reciba un
RequestDelegate(el puntero al “siguiente” middleware). - Tener un método
InvokeAsyncque reciba elHttpContext.
using System.Diagnostics;
public class ResponseTimeMiddleware
{
private readonly RequestDelegate _next;
// El constructor recibe el delegado para llamar al siguiente eslabón
public ResponseTimeMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context)
{
// 1. Lógica ANTES: Arrancamos el cronómetro
var watch = Stopwatch.StartNew();
// 2. Llamamos al siguiente middleware
await _next(context);
// 3. Lógica DESPUÉS: Paramos crono y logueamos
watch.Stop();
var tiempo = watch.ElapsedMilliseconds;
// Escribimos en la consola (o podríamos guardar en BBDD)
Console.WriteLine($"La petición tardó: {tiempo} ms");
}
}
Paso 2: Crear el método de extensión
Para que en el Program.cs quede elegante (estilo app.UseResponseTime()), es buena práctica crear un método de extensión.
public static class ResponseTimeMiddlewareExtensions
{
public static IApplicationBuilder UseResponseTime(this IApplicationBuilder builder)
{
return builder.UseMiddleware<ResponseTimeMiddleware>();
}
}
Paso 3: Registrarlo en el Pipeline
Ahora volvemos al Program.cs y lo usamos como uno más de la familia.
var app = builder.Build();
// Nuestro middleware personalizado
app.UseResponseTime();
app.MapGet("/", async () =>
{
await Task.Delay(500); // Simulamos trabajo lento
return "Hola Mundo";
});
app.Run();
Si ejecutas la API y llamas al endpoint, verás en la consola: La petición tardó: 508 ms
