Escritura de middleware de ASP.NET Core personalizado
Nota:
Esta no es la versión más reciente de este artículo. Para la versión actual, consulte la versión de .NET 9 de este artículo.
Advertencia
Esta versión de ASP.NET Core ya no se admite. Para obtener más información, consulta la Directiva de soporte técnico de .NET y .NET Core. Para la versión actual, consulta la versión .NET 8 de este artículo.
Importante
Esta información hace referencia a un producto en versión preliminar, el cual puede sufrir importantes modificaciones antes de que se publique la versión comercial. Microsoft no proporciona ninguna garantía, expresa o implícita, con respecto a la información proporcionada aquí.
Para la versión actual, consulte la versión de .NET 9 de este artículo.
De Fiyaz Hasan, Rick Anderson y Steve Smith
El software intermedio es un software que se ensambla en una canalización de una aplicación para controlar las solicitudes y las respuestas. ASP.NET Core proporciona un completo conjunto de componentes de middleware integrados, pero en algunos escenarios es posible que quiera escribir middleware personalizado.
En este tema se describe cómo escribir middleware basado en convenciones. Puede encontrar un enfoque donde se usa el establecimiento de tipos seguros y la activación por solicitud en Activación de middleware basada en fábrica en ASP.NET Core.
Clase de middleware
El middleware normalmente está encapsulado en una clase y se expone con un método de extensión. Considere el siguiente middleware en línea, que establece la referencia cultural de la solicitud actual desde una cadena de consulta:
using System.Globalization;
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.UseHttpsRedirection();
app.Use(async (context, next) =>
{
var cultureQuery = context.Request.Query["culture"];
if (!string.IsNullOrWhiteSpace(cultureQuery))
{
var culture = new CultureInfo(cultureQuery);
CultureInfo.CurrentCulture = culture;
CultureInfo.CurrentUICulture = culture;
}
// Call the next delegate/middleware in the pipeline.
await next(context);
});
app.Run(async (context) =>
{
await context.Response.WriteAsync(
$"CurrentCulture.DisplayName: {CultureInfo.CurrentCulture.DisplayName}");
});
app.Run();
El middleware en línea resaltado anterior se usa para demostrar la creación de un componente de middleware mediante una llamada a Microsoft.AspNetCore.Builder.UseExtensions.Use. El método de extensión Use
anterior agrega un delegado de middleware definido en línea a la canalización de solicitudes de la aplicación.
Hay dos sobrecargas disponibles para la extensión Use
:
- Una toma un elemento HttpContext y un elemento
Func<Task>
. Invoque el elementoFunc<Task>
sin ningún parámetro. - La otra toma un elemento
HttpContext
y un elemento RequestDelegate. Invoque el elementoRequestDelegate
pasando el elementoHttpContext
.
Es preferible usar la última sobrecarga, ya que guarda dos asignaciones por solicitud internas que son necesarias cuando se usa la otra sobrecarga.
Pruebe el middleware pasando la referencia cultural. Por ejemplo, solicite https://localhost:5001/?culture=es-es
.
Para información sobre la compatibilidad integrada con la localización, consulte Globalización y localización en ASP.NET Core.
El código siguiente mueve el delegado de middleware a una clase:
using System.Globalization;
namespace Middleware.Example;
public class RequestCultureMiddleware
{
private readonly RequestDelegate _next;
public RequestCultureMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context)
{
var cultureQuery = context.Request.Query["culture"];
if (!string.IsNullOrWhiteSpace(cultureQuery))
{
var culture = new CultureInfo(cultureQuery);
CultureInfo.CurrentCulture = culture;
CultureInfo.CurrentUICulture = culture;
}
// Call the next delegate/middleware in the pipeline.
await _next(context);
}
}
La clase de middleware debe incluir:
- Un constructor público con un parámetro de tipo RequestDelegate.
- Un método público llamado
Invoke
oInvokeAsync
. Este método debe:- Devolver
Task
. - Aceptar un primer parámetro de tipo HttpContext.
- Devolver
Los parámetros adicionales para el constructor y Invoke
/InvokeAsync
se rellenan mediante la inserción de dependencias (DI).
Normalmente, se crea un método de extensión para exponer el middleware a mediante IApplicationBuilder:
using System.Globalization;
namespace Middleware.Example;
public class RequestCultureMiddleware
{
private readonly RequestDelegate _next;
public RequestCultureMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context)
{
var cultureQuery = context.Request.Query["culture"];
if (!string.IsNullOrWhiteSpace(cultureQuery))
{
var culture = new CultureInfo(cultureQuery);
CultureInfo.CurrentCulture = culture;
CultureInfo.CurrentUICulture = culture;
}
// Call the next delegate/middleware in the pipeline.
await _next(context);
}
}
public static class RequestCultureMiddlewareExtensions
{
public static IApplicationBuilder UseRequestCulture(
this IApplicationBuilder builder)
{
return builder.UseMiddleware<RequestCultureMiddleware>();
}
}
El código siguiente llama al middleware desde Program.cs
:
using Middleware.Example;
using System.Globalization;
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.UseHttpsRedirection();
app.UseRequestCulture();
app.Run(async (context) =>
{
await context.Response.WriteAsync(
$"CurrentCulture.DisplayName: {CultureInfo.CurrentCulture.DisplayName}");
});
app.Run();
Dependencias de middleware
El middleware debería seguir el principio de dependencias explicitas mediante la exposición de sus dependencias en el constructor. El middleware se construye una vez por duración de la aplicación.
Los componentes de software intermedio pueden resolver sus dependencias de una inserción de dependencias (DI) mediante parámetros del constructor. UseMiddleware también puede aceptar parámetros adicionales directamente.
Dependencias de middleware bajo solicitud
El middleware se construye al inicio de la aplicación y, por tanto, tiene la duración de la aplicación. Los servicios de duración limitada que usan los constructores de software intermedio no se comparten con otros tipos insertados mediante dependencias durante cada solicitud. Para compartir un servicio limitado entre el middleware y otros tipos, agregue esos servicios a la signatura del método InvokeAsync
. El método InvokeAsync
puede aceptar parámetros adicionales que la inserción de dependencias propaga:
namespace Middleware.Example;
public class MyCustomMiddleware
{
private readonly RequestDelegate _next;
public MyCustomMiddleware(RequestDelegate next)
{
_next = next;
}
// IMessageWriter is injected into InvokeAsync
public async Task InvokeAsync(HttpContext httpContext, IMessageWriter svc)
{
svc.Write(DateTime.Now.Ticks.ToString());
await _next(httpContext);
}
}
public static class MyCustomMiddlewareExtensions
{
public static IApplicationBuilder UseMyCustomMiddleware(
this IApplicationBuilder builder)
{
return builder.UseMiddleware<MyCustomMiddleware>();
}
}
Las opciones de registro y duración contienen un ejemplo completo de middleware con servicios de duración con ámbito.
El código siguiente se usa para probar el middleware anterior:
using Middleware.Example;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddScoped<IMessageWriter, LoggingMessageWriter>();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseMyCustomMiddleware();
app.MapGet("/", () => "Hello World!");
app.Run();
La interfaz y la implementación de IMessageWriter
:
namespace Middleware.Example;
public interface IMessageWriter
{
void Write(string message);
}
public class LoggingMessageWriter : IMessageWriter
{
private readonly ILogger<LoggingMessageWriter> _logger;
public LoggingMessageWriter(ILogger<LoggingMessageWriter> logger) =>
_logger = logger;
public void Write(string message) =>
_logger.LogInformation(message);
}
Recursos adicionales
- Ejemplo de código usado en este artículo
- Origen UseExtensions en GitHub
- Las opciones de registro y duración contienen un ejemplo completo de middleware con servicios de duración con ámbito, transitoriosy singleton.
- ANÁLISIS EN PROFUNDIDAD: ¿CÓMO SE COMPILA LA CANALIZACIÓN DE MIDDLEWARE PRINCIPAL DE ASP.NET?
- Middleware de ASP.NET Core
- Prueba del middleware de ASP.NET Core
- Migración de controladores y módulos HTTP a middleware de ASP.NET Core
- Inicio de la aplicación en ASP.NET Core
- Características de solicitud de ASP.NET Core
- Activación de middleware basada en Factory en ASP.NET Core
- Activación de middleware con un contenedor de terceros en ASP.NET Core
Por Rick Anderson y Steve Smith
El software intermedio es un software que se ensambla en una canalización de una aplicación para controlar las solicitudes y las respuestas. ASP.NET Core proporciona un completo conjunto de componentes de middleware integrados, pero en algunos escenarios es posible que quiera escribir middleware personalizado.
Nota:
En este tema se describe cómo escribir middleware basado en convenciones. Puede encontrar un enfoque donde se usa el establecimiento de tipos seguros y la activación por solicitud en Activación de middleware basada en fábrica en ASP.NET Core.
Clase de middleware
El middleware normalmente está encapsulado en una clase y se expone con un método de extensión. Use el siguiente software intermedio a modo de ejemplo. En este se establece la referencia cultural de la solicitud actual a partir de la cadena de solicitud:
public class Startup
{
public void Configure(IApplicationBuilder app)
{
app.Use(async (context, next) =>
{
var cultureQuery = context.Request.Query["culture"];
if (!string.IsNullOrWhiteSpace(cultureQuery))
{
var culture = new CultureInfo(cultureQuery);
CultureInfo.CurrentCulture = culture;
CultureInfo.CurrentUICulture = culture;
}
// Call the next delegate/middleware in the pipeline
await next();
});
app.Run(async (context) =>
{
await context.Response.WriteAsync(
$"Hello {CultureInfo.CurrentCulture.DisplayName}");
});
}
}
El código de ejemplo anterior se usa para mostrar la creación de un componente de software intermedio. Para información sobre la compatibilidad integrada con la localización, consulte Globalización y localización en ASP.NET Core.
Pruebe el middleware pasando la referencia cultural. Por ejemplo, solicite https://localhost:5001/?culture=no
.
El código siguiente mueve el delegado de middleware a una clase:
using Microsoft.AspNetCore.Http;
using System.Globalization;
using System.Threading.Tasks;
namespace Culture
{
public class RequestCultureMiddleware
{
private readonly RequestDelegate _next;
public RequestCultureMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context)
{
var cultureQuery = context.Request.Query["culture"];
if (!string.IsNullOrWhiteSpace(cultureQuery))
{
var culture = new CultureInfo(cultureQuery);
CultureInfo.CurrentCulture = culture;
CultureInfo.CurrentUICulture = culture;
}
// Call the next delegate/middleware in the pipeline
await _next(context);
}
}
}
La clase de middleware debe incluir:
- Un constructor público con un parámetro de tipo RequestDelegate.
- Un método público llamado
Invoke
oInvokeAsync
. Este método debe:- Devolver
Task
. - Aceptar un primer parámetro de tipo HttpContext.
- Devolver
Los parámetros adicionales para el constructor y Invoke
/InvokeAsync
se rellenan mediante la inserción de dependencias (DI).
Dependencias de middleware
El middleware debería seguir el principio de dependencias explicitas mediante la exposición de sus dependencias en el constructor. El middleware se construye una vez por duración de la aplicación. Si necesita compartir servicios con software intermedio en una solicitud, vea la sección Dependencias de middleware bajo solicitud.
Los componentes de software intermedio pueden resolver sus dependencias de una inserción de dependencias (DI) mediante parámetros del constructor. UseMiddleware también puede aceptar parámetros adicionales directamente.
Dependencias de middleware bajo solicitud
Dado que el software intermedio se construye al inicio de la aplicación y no bajo solicitud, los servicios de duración con ámbito que usan los constructores de software intermedio no se comparten con otros tipos insertados mediante dependencias durante cada solicitud. Si debe compartir un servicio con ámbito entre su middleware y otros tipos, agregue esos servicios a la signatura del método InvokeAsync
. El método InvokeAsync
puede aceptar parámetros adicionales que la inserción de dependencias propaga:
public class CustomMiddleware
{
private readonly RequestDelegate _next;
public CustomMiddleware(RequestDelegate next)
{
_next = next;
}
// IMyScopedService is injected into InvokeAsync
public async Task InvokeAsync(HttpContext httpContext, IMyScopedService svc)
{
svc.MyProperty = 1000;
await _next(httpContext);
}
}
Las opciones de registro y duración contienen un ejemplo completo de middleware con servicios de duración con ámbito.
Método de extensión de middleware
El método de extensión siguiente expone el software intermedio mediante IApplicationBuilder:
using Microsoft.AspNetCore.Builder;
namespace Culture
{
public static class RequestCultureMiddlewareExtensions
{
public static IApplicationBuilder UseRequestCulture(
this IApplicationBuilder builder)
{
return builder.UseMiddleware<RequestCultureMiddleware>();
}
}
}
El código siguiente llama al middleware desde Startup.Configure
:
public class Startup
{
public void Configure(IApplicationBuilder app)
{
app.UseRequestCulture();
app.Run(async (context) =>
{
await context.Response.WriteAsync(
$"Hello {CultureInfo.CurrentCulture.DisplayName}");
});
}
}
Recursos adicionales
- Las opciones de registro y duración contienen un ejemplo completo de middleware con servicios de duración con ámbito, transitoriosy singleton.
- Middleware de ASP.NET Core
- Prueba del middleware de ASP.NET Core
- Migración de controladores y módulos HTTP a middleware de ASP.NET Core
- Inicio de la aplicación en ASP.NET Core
- Características de solicitud de ASP.NET Core
- Activación de middleware basada en Factory en ASP.NET Core
- Activación de middleware con un contenedor de terceros en ASP.NET Core