Escritura de middleware personalizado
Cree middleware en línea con Use/Run/Map y middleware basado en clases con InvokeAsync para obtener componentes reutilizables.
Escritura de middleware personalizado es una lección gratuita de C# Academy en CoddyKit. Esta es la lección 2 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de C# Academy, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de C# Academy incluye 4 lecciones en total.
¿Por qué crear middleware personalizado?
El middleware personalizado gestiona aspectos transversales que se aplican a muchas solicitudes: registro, medición de tiempos, gestión de errores, autenticación, compresión y almacenamiento en caché. Escribirlo una vez hace que la lógica se ejecute automáticamente para cada solicitud coincidente.
Middleware insertado directamente con Use
La forma más rápida de agregar middleware es pasar un delegado a app.Use(). Es adecuado para crear prototipos o para lógica realmente sencilla.
app.Use(async (context, next) =>
{
var start = DateTimeOffset.UtcNow;
await next(context);
var elapsed = DateTimeOffset.UtcNow - start;
context.Response.Headers.Append(
"X-Elapsed", elapsed.TotalMilliseconds.ToString("F0") + "ms");
});Middleware basado en clases (convención)
Una clase de middleware convencional tiene un constructor que recibe RequestDelegate y un método público InvokeAsync. Los parámetros adicionales del constructor se pueden inyectar desde DI.
public class SecurityHeadersMiddleware
{
private readonly RequestDelegate _next;
public SecurityHeadersMiddleware(RequestDelegate next) => _next = next;
public async Task InvokeAsync(HttpContext context)
{
var headers = context.Response.Headers;
headers.Append("X-Content-Type-Options", "nosniff");
headers.Append("X-Frame-Options", "DENY");
headers.Append("X-XSS-Protection", "1; mode=block");
headers.Append("Referrer-Policy", "strict-origin-when-cross-origin");
await _next(context);
}
}
// Extension method for clean registration:
public static class SecurityHeadersExtensions
{
public static IApplicationBuilder UseSecurityHeaders(
this IApplicationBuilder app) =>
app.UseMiddleware<SecurityHeadersMiddleware>();
}IMiddleware para dependencias con ámbito
Si el middleware necesita servicios con ámbito, como DbContext, implemente IMiddleware. Regístrelo con ámbito y el framework lo resolverá de nuevo para cada solicitud.
public class AuditMiddleware : IMiddleware
{
private readonly AuditDbContext _db;
private readonly IHttpContextAccessor _http;
public AuditMiddleware(AuditDbContext db, IHttpContextAccessor http)
{
_db = db;
_http = http;
}
public async Task InvokeAsync(HttpContext ctx, RequestDelegate next)
{
await next(ctx);
if (ctx.Request.Method != "GET")
{
_db.AuditLogs.Add(new AuditLog
{
User = ctx.User.Identity?.Name,
Path = ctx.Request.Path,
Status = ctx.Response.StatusCode,
At = DateTime.UtcNow
});
await _db.SaveChangesAsync();
}
}
}
builder.Services.AddScoped<AuditMiddleware>();
app.UseMiddleware<AuditMiddleware>();Middleware con opciones
Pase la configuración al middleware mediante un objeto de opciones en el constructor. Registre las opciones en el contenedor de DI o páselas directamente al registrar el middleware.
public class ThrottleOptions
{
public int MaxRequestsPerSecond { get; set; } = 100;
public string[] ExcludedPaths { get; set; } = Array.Empty<string>();
}
public class ThrottleMiddleware
{
private readonly RequestDelegate _next;
private readonly ThrottleOptions _options;
public ThrottleMiddleware(RequestDelegate next, ThrottleOptions options)
{
_next = next;
_options = options;
}
// ...
}
// Registration with options:
app.UseMiddleware<ThrottleMiddleware>(new ThrottleOptions
{
MaxRequestsPerSecond = 50
});Middleware para la gestión de excepciones
Un middleware personalizado para gestionar excepciones captura todas las excepciones no controladas y devuelve respuestas de error estructuradas.
public class GlobalExceptionMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<GlobalExceptionMiddleware> _logger;
public GlobalExceptionMiddleware(RequestDelegate next, ILogger<GlobalExceptionMiddleware> l)
{
_next = next;
_logger = l;
}
public async Task InvokeAsync(HttpContext ctx)
{
try { await _next(ctx); }
catch (NotFoundException ex)
{
ctx.Response.StatusCode = 404;
await ctx.Response.WriteAsJsonAsync(new { error = ex.Message });
}
catch (Exception ex)
{
_logger.LogError(ex, "Unhandled exception");
ctx.Response.StatusCode = 500;
await ctx.Response.WriteAsJsonAsync(new { error = "Internal server error" });
}
}
}Middleware de registro de solicitudes
Un middleware estructurado de registro de solicitudes y respuestas registra el método, la ruta, el código de estado y el tiempo transcurrido de cada solicitud.
public class RequestLoggingMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<RequestLoggingMiddleware> _log;
public RequestLoggingMiddleware(RequestDelegate n, ILogger<RequestLoggingMiddleware> l)
{ _next = n; _log = l; }
public async Task InvokeAsync(HttpContext ctx)
{
var sw = System.Diagnostics.Stopwatch.StartNew();
await _next(ctx);
sw.Stop();
_log.LogInformation(
"{Method} {Path} -> {Status} in {Ms}ms",
ctx.Request.Method,
ctx.Request.Path,
ctx.Response.StatusCode,
sw.ElapsedMilliseconds);
}
}Middleware condicional con UseWhen
UseWhen bifurca el pipeline de forma condicional según un predicado, sin dividirlo permanentemente. Después, la bifurcación vuelve a unirse al pipeline principal.
// Apply authentication middleware only to /api/* routes
app.UseWhen(
ctx => ctx.Request.Path.StartsWithSegments("/api"),
apiApp =>
{
apiApp.UseAuthentication();
apiApp.UseAuthorization();
});
// Public routes (/public/*) bypass authentication
app.MapGet("/public/health", () => "OK");
app.MapGet("/api/data", [Authorize] async (DataService s) => await s.GetAsync());Middleware de almacenamiento en caché de respuestas
El middleware integrado de almacenamiento en caché de respuestas guarda las respuestas completas en memoria. Configure las políticas de caché por endpoint mediante encabezados o atributos.
builder.Services.AddResponseCaching();
app.UseResponseCaching();
app.MapGet("/products", async (AppDbContext db) =>
{
var products = await db.Products.AsNoTracking().ToListAsync();
return Results.Ok(products);
})
.CacheOutput(p => p.Expire(TimeSpan.FromMinutes(2)).Tag("products"));Caso real: middleware del modo de mantenimiento
Un middleware de modo de mantenimiento que lee un indicador de la configuración, con compatibilidad para la recarga en caliente mediante IOptionsMonitor, y rechaza las solicitudes que no son comprobaciones de estado durante el mantenimiento.
public class MaintenanceMiddleware
{
private readonly RequestDelegate _next;
private readonly IOptionsMonitor<MaintenanceOptions> _options;
public MaintenanceMiddleware(RequestDelegate next,
IOptionsMonitor<MaintenanceOptions> opts)
{ _next = next; _options = opts; }
public async Task InvokeAsync(HttpContext ctx)
{
if (_options.CurrentValue.IsEnabled
&& !ctx.Request.Path.StartsWithSegments("/health"))
{
ctx.Response.StatusCode = 503;
ctx.Response.Headers.Append("Retry-After", "300");
await ctx.Response.WriteAsJsonAsync(
new { message = "Service under maintenance" });
return;
}
await _next(ctx);
}
}Comprobación rápida
¿Por qué debería implementar IMiddleware en lugar del enfoque basado en convenciones cuando necesita un servicio con ámbito, como DbContext?
Resumen: escritura de middleware personalizado
Conclusiones clave:
- Insertado directamente (delegado de Use): rápido y adecuado para una sola línea; basado en clases: reutilizable y comprobable
- Clase convencional: el constructor recibe RequestDelegate; InvokeAsync(HttpContext)
- IMiddleware: resolución de DI por solicitud, necesaria para las dependencias con ámbito
- UseWhen: bifurcación condicional que vuelve a unirse al pipeline principal
- Escriba primero el middleware de gestión de excepciones, en la posición más externa, para capturar todos los errores
- Cree métodos de extensión (UseXxx) para un registro claro y fácil de descubrir
Preguntas frecuentes
¿La lección «Escritura de middleware personalizado» es gratis?
Sí — el texto completo de «Escritura de middleware personalizado» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de C# Academy, actualiza a CoddyKit PRO. El curso de C# Academy incluye 4 lecciones en total.
¿Qué aprenderé en «Escritura de middleware personalizado»?
Cree middleware en línea con Use/Run/Map y middleware basado en clases con InvokeAsync para obtener componentes reutilizables. Practicas C# Academy con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.
¿Necesito experiencia previa para empezar C# Academy?
No se requiere experiencia previa. C# Academy en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 2 de 4.
¿Cuánto tiempo toma la lección «Escritura de middleware personalizado»?
La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.
¿Puedo escribir y ejecutar código en esta lección de C# Academy?
Sí. Cada lección de C# Academy incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.
Todas las lecciones de este curso
- Descripción general de la canalización de ASP.NET Core
- Escritura de middleware personalizado
- Cortocircuito y ramificación
- Orden del middleware y middleware integrado