Middleware en ASP.NET 6: clases de middleware personalizadas

Estrada Web Group
Estrada Web Group
Middleware en ASP.NET 6: clases de middleware personalizadas

Esta publicación es la parte 2 de una serie de cuatro partes. Es posible que desee leer la Parte 1 primero o ver la parte 3 o la parte 4.

En la publicación anterior, hablamos sobre qué es Middleware, para qué sirve y formas simples de incluirlo en la canalización (pipeline) de nuestra aplicación ASP.NET 6.

En esta publicación, ampliaremos estos fundamentos para crear algunas clases de Middleware personalizadas.

La arquitectura estándar de middleware

A diferencia de lo que hicimos en la Parte 1, la mayoría de las veces queremos que nuestro Middleware sean clases separadas y no solo líneas adicionales en nuestro archivo Program.cs.

¿Recuerdas este middleware de la Parte 1? ¿El que devolvió una respuesta que consistía en "¡Hola, bienvenido a Estrada Web Group!"?

//...Rest of Program.cs

app.Run(async context =>
{
    await context.Response.WriteAsync("!Hola, bienvenido a Estrada Web Group!");
});

//...Rest of Program.cs

Vamos a crear una clase de middleware personalizada que haga lo mismo.

Conceptos básicos de una clase middleware

Aquí hay una clase vacía básica que usaremos para este middleware:

namespace MiddlewareNET6Demo.Middleware
{
    public class SimpleResponseMiddleware
    {
    }
}

Una clase de middleware consta de tres partes. Primero, cualquier clase middleware en ASP.NET 6 debe incluir una instancia privada de RequestDelegate que se completa con el constructor de la clase. Recuerde que RequestDelegate representa la siguiente pieza de middleware en la canalización:

namespace MiddlewareNET6Demo.Middleware
{
    private readonly RequestDelegate _next;

    public SimpleResponseMiddleware(RequestDelegate next)
    {
        _next = next;
    }
}

En segundo lugar, la clase debe tener un método asíncrono InvokeAsync() que tome una instancia de HttpContext como su primer parámetro.

public class SimpleResponseMiddleware
{
    private readonly RequestDelegate _next;

    public SimpleResponseMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        //...Implementation
    }
}

Tercero, el middleware debe tener su propia implementación única. Para este middleware, todo lo que queremos hacer es devolver una respuesta personalizada:

public class SimpleResponseMiddleware
{
    private readonly RequestDelegate _next;

    public SimpleResponseMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        await context.Response.WriteAsync("!Hola, bienvenido a Estrada Web Group!");
    }
}

NOTA: dentro del método InvokeAsync(), la mayoría del middleware tendrá una llamada para esperar el siguiente (context); cualquier middleware que no haga esto será un middleware de terminal, porque la canalización más allá de ese punto no se ejecutará. Dado que nuestro SimpleResponseMiddleware, de hecho, desea detener el procesamiento de la canalización, no llama a la espera siguiente (context).

Agregar el middleware a la canalización

En este punto, es posible que conectemos esta clase de middleware a nuestra aplicación en el archivo Program.cs usando el método UseMiddleware<T>():

//...Rest of Program.cs

app.UseMiddleware<LayoutMiddleware>();

//...Rest of Program.cs

Para clases middleware muy simples, esto es suficiente. Sin embargo, a menudo usamos métodos de extensión en lugar de UseMiddleware<T>() porque nos brindan otra capa de abstracción:

namespace MiddlewareNET6Demo.Extensions
{
    public static class MiddlewareExtensions
    {
        public static IApplicationBuilder UseSimpleResponseMiddleware(this IApplicationBuilder builder)
        {
            return builder.UseMiddleware<SimpleResponseMiddleware>();
        }
    }
}

Entonces usaríamos ese método de extensión así:

//...Rest of Program.cs

app.UseSimpleResponseMiddleware();

//...Rest of Program.cs

Cualquiera de las dos formas de hacer esto es correcta. Personalmente, prefiero la claridad y la legibilidad de la ruta del método de extensión, pero puedes elegir lo que más te guste.

Creación de un middleware de registro

Uno de los escenarios más comunes para el middleware es el registro, específicamente el registro de cosas como la ruta de la solicitud o los encabezados, la referencia cultural o el cuerpo de la respuesta.

La clase de servicio de registro

Vamos a construir una clase de middleware de registro que haga dos cosas:

  • Registre la ruta de la solicitud.
  • Registre los distintos encabezados de respuesta.

Primero, debemos crear una interfazILoggingService y una clase LoggingService.

namespace MiddlewareNET6Demo.Logging
{
    public class LoggingService : ILoggingService
    {

        public void Log(LogLevel logLevel, string message)
        {
            //...Implementation for logging
        }
    }

    public interface ILoggingService
    {
        public void Log(LogLevel level, string message);
    }
}

Luego, debemos agregar LoggingService a la colección de servicios de la aplicación en Program.cs:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();
builder.Services.AddTransient<ILoggingService, LoggingService>();

//...Rest of Program.cs

La clase middleware pueden tener servicios inyectados en ellas, al igual que las clases normales.

La clase LoggingMiddleware

Ahora necesitamos una clase LoggingMiddleware que realmente realice el registro. Primero, creemos un esqueleto para la clase LoggingMiddleware, que acepta una instancia de ILoggingService como parámetro en el constructor:

namespace MiddlewareNET6Demo.Middleware
{
    public class LoggingMiddleware
    {
        private readonly RequestDelegate _next;
        private readonly ILoggingService _logger;

        public LoggingMiddleware(RequestDelegate next, ILoggingService logger)
        {
            _next = next;
            _logger = logger;
        }

        public async Task InvokeAsync(HttpContext context)
        {
            //...Implementation
        }
    }
}

Recuerda que estamos registrando la ruta de la solicitud y los encabezados únicos de la respuesta. Eso significa que tendremos código a ambos lados de la llamada para esperar al siguiente (context).

namespace MiddlewareNET6Demo.Middleware
{
    public class LoggingMiddleware
    {
        //...Rest of implementation

        public async Task InvokeAsync(HttpContext context)
        {
            //Log the incoming request path
            _logger.Log(LogLevel.Information, context.Request.Path);

            //Invoke the next middleware in the pipeline
            await _next(context);

            //Get distinct response headers
            var uniqueResponseHeaders
                = context.Response.Headers
                                  .Select(x => x.Key)
                                  .Distinct();

            //Log these headers
            _logger.Log(LogLevel.Information, string.Join(", ", uniqueResponseHeaders));
        }
    }
}

Agregar LoggingMiddleware a la canalización

Como prefiero usar métodos de extensión para agregar middleware a la canalización, creemos uno nuevo:

namespace MiddlewareNET6Demo.Extensions
{
    public static class MiddlewareExtensions
    {
        //...Rest of implementation
    
        public static IApplicationBuilder UseLoggingMiddleware(this IApplicationBuilder builder)
        {
            return builder.UseMiddleware<LoggingMiddleware>();
        }
    }
}

Finalmente, debemos llamar a nuestro nuevo método de extensión en Program.cs para agregar la clase LoggingMiddleware a la canalización:

//...Rest of Program.cs

app.UseLoggingMiddleware();

//...Rest of Program.cs

Cuando ejecutamos la aplicación, podemos ver (estableciendo puntos de interrupción) que el código registrará correctamente tanto la ruta de la solicitud como los encabezados de respuesta.

Middleware en ASP.NET 6: clases de middleware personalizadas

Middleware en ASP.NET 6: clases de middleware personalizadas

¿Qué sigue?

En la próxima publicación de esta serie, analizamos el orden de las operaciones para el Middleware, mostramos un nuevo Middleware que registra el tiempo de ejecución y analizamos algunas de las dificultades comunes que pueden ocurrir al crear la canalización de Middleware.

¡Feliz codificación!

 

Compartir artículo:

Más artículos geniales

Aprende a integrar pagos con PayPal en ASP.NET Core: Crear el proyecto web ASP.NET Core

Aprende a integrar pagos con PayPal en ASP.NET Core: Crear el proyecto web ASP.NET Core

En este artículo creare el proyecto ASP.NET Core para integrar PayPal y solicitar pagos en mi sitio web.

Ver artículo completo
Cifrar y descifrar contraseñas o cadenas de texto en C#

Cifrar y descifrar contraseñas o cadenas de texto en C#

Encriptar o cifrar y descifrar contraseñas o cadenas de texto utilizando una clave simétrica en C #

Ver artículo completo

Manténgase actualizado

Obtenga excelente contenido en su bandeja de entrada todas las semanas.
Solo contenido excelente, no compartimos su correo electrónico con terceros.
Subir al inicio de la pantalla