Middlewares y Filtros en .NET

15 Jul 2021 15 min (0) Comentarios

En este post vamos a ver que son los middleware en .NET así como los filtros (FilterAttribute), veremos su diferencia y cómo implementarlos;

 

 

1 - Qué es un middleware en .NET?

Para poder ver las diferencias entre middleware y filtros, debemos entender que es cada cosa. 

Primero decir que el nombre de “middleware” yo lo odio, porque no representa claramente donde está ubicado, osea si, en el medio. Pero a mi personalmente no me dice nada. 

 

En .NET un middleware es una clase que permite manipular una petición (o respuesta) HTTP.

 

Un middleware actúa en cada request que llega a nuestra aplicación, por lo tanto es parte de la request pipeline.

Y hacen lo propio con la respuesta, pero en orden inverso.

request pipeline

Desde la llegada de .NET Core el uso de middleware es muy común, por lo que es muy importante saber la lógica que tienen detrás y su funcionamiento.

Cabe destacar que el orden es importante, ya que como he indicado se van ejecutando uno detrás de otro. 

 

Hay que tener en cuenta, que podemos crear un middleware que en ciertas condiciones nos crea una respuesta directamente, esta acción lo que haría sería evitar que los middleware que vienen después no se ejecuten. 

 

 

1.1 - Ejemplos de middleware en ASP.NET Core

Tenemos ejemplos de middlewares cuando creamos una API con el template de visual studio nos viene con la instrucción app.UseHttpsRedirection(); que lo que hace por detrás es añadir un middleware para redireccionar todas las peticiones HTTP a HTTPS.

CORS o routing son otras funcionalidades muy comunes las cuales también se implementan a través de middlewares.

 

O por ejemplo si tenemos activado app.UseStaticFiles(), el cual ubicamos normalmente el primero, si la request es “get fichero” como un css o javascript, detectaremos que el fichero está en la carpeta de ficheros estáticos y lo devolveremos, sin ejecutar el resto de los middlewares. 

response from middleware

 

1.2 - Crear un middleware en .NET

Para crear un middleware en .NET lo único que tenemos que hacer es una clase normal, con la excepción de dos reglas

  1. El constructor debe recibir RequestDelegate.
  2. Un método llamado Invoke que reciba HttpContext como parámetro.
public class EjemploMiddleware
{
    private readonly RequestDelegate _next;

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

    public async Task Invoke(HttpContext context)
    {
      await _next(context);
    }
}

Y en la clase podemos hacer lo que queramos, incluso podemos acceder al contendor de dependenicas a través de context.RequestServices.GetService<T>() o a través de la inyeccion de dependencias normal a través del constructor.

 

Ahora añadimos lógica, en el ejemplo es sencillo, loguear el tiempo que tarda la request, pero lo podemos complicar tanto como queramos.

public class EjemploMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger _logger;


    public EjemploMiddleware(RequestDelegate next, ILoggerFactory loggerFactory)
    {
        _next = next;
        _logger = loggerFactory.CreateLogger(typeof(EjemploMiddleware));
    }

    public async Task Invoke(HttpContext context)
    {
        //Este id es para simular un ID que viene del FrontEnd/Container
        //Con el cual mantenemos "trace" de toda la acción del usuario
        //Antes de la request
        Guid traceId = Guid.NewGuid();
        _logger.LogDebug($"Request {traceId} iniciada");
        Stopwatch stopWatch = new Stopwatch();
        stopWatch.Start();


        await _next(context);

        //Despues de la request
        stopWatch.Stop();

        TimeSpan ts = stopWatch.Elapsed;
        string elapsedTime = String.Format("{0:00}:{1:00}:{2:00}.{3:00}",
        ts.Hours, ts.Minutes, ts.Seconds, ts.Milliseconds / 10);
        _logger.LogDebug($"La request {traceId} ha llevado {elapsedTime} ");
    }
}

Para activar nuestro middleware debemos hacerlo en el método Configure dentro de startup.cs y para ello únicamente incluiremos el siguiente código.

app.UseMiddleware<EjemploMiddleware>();
  • Nota: en Blazor WA no podemos indicar middlewares porque no hay pipeline. 

 

Y con esto ya tenemos nuestro middleware activado y funcionando, si hacemos una request podemos ver el resultado:

resultado middleware .net

 

 

2 - Qué es un filtro en .NET

Primero de todo decir que cuando decimos filtro nos referimos a un  ActionFilterAttribute.

Y el funcionamiento es similar al del middleware, pero en este caso lo utilizaremos cuando queremos un mayor control o ser más preciso con lo que podemos hacer.

 

Mientras que en el middleware únicamente tenemos acceso al HttpContext, en el ActionFilterAttribute tendremos acceso a mucha más información, como puede ser el modelo y por supuesto también al HttpContext.

filterattribute

Si pusiéramos en una imagen la pipeline de los middleware y la de los filtros la de los filtros iría después de la de los middleware, pero a diferencia de en los middleware, en los filtros podemos especificar donde queremos utilizarlos.

Por ello indicamos que son más específicos porque puede ser que queramos que un endpoint tenga un filtro o no:

middleware y filtros en la pipeline de .net

Como podemos ver en la imagen, el filtro 2 es común para ambos endpoints, mientras que el filtro 1 y el filtro 3 son exclusivos de uno de los endpoints. 

  • Nota: ActionFilterAttribute no es el único filtro en .NET, como ExceptionFilter que se activa al tener una excepción.

 

 

2.1 - Ejemplos de filtros en ASP.NET 

Los ejemplos más comunes sonAuthorize, el cual es un action filter que nos permite restringir el acceso a un endpoint dependiendo del usuario o del rol del mismo.

  • Nota: crearé un post sobre el pronto. 

Otro ejemplo muy común es OutPutCache el cual mantiene en cache la respuesta del endpoint por el tiempo que especifiquemos. 

 

Por supuesto la pipeline de los filtros funciona igual que la de los middleware, podemos devolver la respuesta desde uno de ellos. El caso más común es Authorize. Con la siguiente directiva [Authorize(Policy = "PuedeConsultarUsuarios")] el usuario logueado neceista esa policy para poder acceder al endpoint.

 

 

2.3 Implementar un filtro en ASP.NET

Para crear un filtro es muy sencillo, primero debemos crear una clase que implemente cualquiera de los filtros que proporciona .NET, en nuestro caso ActionFilterAttribute.

 

Igual que en el middleware tenemos acceso a la inyección de dependencias desde el constructor. 

 

Y posteriormente sobreescribimos los metodos que neceistemos, OnActionExecuting se ejecutará antes del endpoint, y OnActionExecuted después.

Aquí puedes ver un ejemplo:

public class EjemploFiltro : ActionFilterAttribute
{
    private readonly ILogger<EjemploFiltro> _logger;

    public EjemploFiltro(ILogger<EjemploFiltro> logger)
    {
        _logger = logger;
    }

    public override void OnActionExecuting(ActionExecutingContext context)
    {
        _logger.LogInformation($"antes del método {context.ActionDescriptor.DisplayName}");
        base.OnActionExecuting(context);
    }

    public override void OnActionExecuted(ActionExecutedContext context)
    {
        _logger.LogInformation($"después del método {context.ActionDescriptor.DisplayName}");
        base.OnActionExecuted(context);
    }
}

En el ejemplo únicamente hace un log del método que estamos ejecutando.

 

 

2.4 - Implementar un filtro Async en ASP.NET

Si estamos accediendo a un método asíncrono debemos utilizar IAsyncActionFilter del cual sobreescribiremos el método OnActionExecutionAsync.

public class EjemploFiltro : IAsyncActionFilter
{
    private readonly ILogger<EjemploFiltro> _logger;

    public EjemploFiltro(ILogger<EjemploFiltro> logger)
    {
        _logger = logger;
    }

    public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
    {
        _logger.LogInformation($"Antes del método {context.ActionDescriptor.DisplayName}");
        await next();
        _logger.LogInformation($"Después del método {context.ActionDescriptor.DisplayName}");

    }
}

Como vemos luce muy similar al middleware.

 

Ahora nos queda añadir un poco de configuración para poder utilizarlo. Primero debemos añadirlo al contenedor de dependencias 

services.AddScoped<EjemploFiltro>();
  • Nota: Necesitamos inyectarlo porque estamos inyectando en el mismo el logger. 

Y posteriormente en el método/Endpoint donde lo queremos utilizar lo indicamos con [ServiceFilter(typeof(EjemploFiltro))] quedando de la siguiente manera:

[ServiceFilter(typeof(EjemploFiltro))]
[HttpGet("{userName}")]
public async Task<ResultDto<PersonalProfileDto>> Get(string userName)
{
    return (await GetProfile(userName)).MapDto(x=>x);
}
  • Nota: si no estuviéramos inyectando el logger, podríamos llamar al filtro directamente con `[EjemploFiltro]`

 

 

Conclusión

En este post hemos visto 

  • Qué es y cómo crear middlewares en .NET
  • Qué es y cómo crear un Filter attribute en .NET
  • Cómo funciona la request pipeline en .NET
  • Ejemplos de código para Middlewares en .NET
  • Ejemplos de código para filtors en .NET

 


Uso del bloqueador de anuncios adblock

Hola!

Primero de todo bienvenido a la web de NetMentor donde podrás aprender programación en C# y .NET desde un nivel de principiante hasta más avanzado.


Yo entiendo que utilices un bloqueador de anuncios como AdBlock, Ublock o el propio navegador Brave. Pero te tengo que pedir por favor que desactives el bloqueador para esta web.


Intento personalmente no poner mucha publicidad, la justa para pagar el servidor y por supuesto que no sea intrusiva; Si pese a ello piensas que es intrusiva siempre me puedes escribir por privado o por Twitter a @NetMentorTW.


Si ya lo has desactivado, por favor recarga la página.


Un saludo y muchas gracias por tu colaboración

© copyright 2024 NetMentor | Todos los derechos reservados | RSS Feed

Buy me a coffee Invitame a un café