Escritura de middleware de ASP.NET Core personalizado

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. Para ver un enfoque que emplea el establecimiento de tipos seguros y la activación por solicitud, consulte Activación de middleware basada en Factory 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 obtener más información sobre la compatibilidad con la localización integrada de ASP.NET Core, vea 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 o InvokeAsync. Este método debe:
    • Devolver Task.
    • Aceptar un primer parámetro de tipo HttpContext.

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<T> 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 Invoke. El método Invoke 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 Invoke
    public async Task Invoke(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