Custom Middleware in ASP.Net 5

Custom Middleware in ASP.Net 5

Durante la ejecución del ciclo de vida de una aplicación en asp.net core contamos con diferentes aplicaciones o rutinas dentro de nuestros proyectos que se ejecutan de forma secuencial dentro del pipeline de la aplicación.

image.png Imagen tomada del sitio de microsoft

En la anterior imagen cada middleware corresponde a un delegado que puede ejecutar diferentes acciones y pasar la ejecución a un siguiente delegado, de esta forma podemos ejecutar metodos antes y despues de realizar la ejecucion por ejemplo de un controlador en un api rest.

En el siguiente ejemplo vamos a implementar un control de excepciones por medio de un middleware que estara en la capacidad de capturar cualquier excepción durante la ejecución del pipeline de la aplicación.

Creando la clase Middleware

En primer lugar definimos la clase que se encargara de realizar la ejecución del siguiente delegado y en caso de presentar un error en las ejecuciones siguientes almacenar registrar la excepcion en una bd o donde sea necesario segun el requerimiento que tengamos.

 public class ExceptionMiddleware
    {
        private readonly RequestDelegate _next;
        private readonly ILogger<ExceptionMiddleware> _logger;
        public ExceptionMiddleware(RequestDelegate next, ILogger<ExceptionMiddleware> logger)
        {         
            _logger = logger;
            _next = next;
        }

        public async Task InvokeAsync(HttpContext context)
        {
            try
            {
                await _next(context);
            }
            catch (Exception ex )
            {
               _logger.LogError(ex.Message);

            }

        }
    }

en la clase anterior, la aplicacion inyectará en el constructor:

RequestDelegate next: El siguiente delegado a ser ejecutado

ILogger logger: El componente que estemos utilizando para registrar las excepciones del sistema

Adicionalmente se debe generar un metodo el cual sera ejecutado por el middleware, este metodo recibe el contexto de la aplicación, en este punto dentro de un bloque try- catch podemos realizar la ejecucion del siguiente elemento del pipeline, si en alguna de las ejecuciones posteriores ocurre una excepcion sera registrado al momento de devolver la ejecucion, un ejemplo puede ser una excepcion no controlada almacenando realizando algun tipo de transaccion en la base de datos.

  public async Task InvokeAsync(HttpContext context)
        {
            try
            {
                await _next(context);
            }
            catch (Exception ex )
            {
               _logger.LogError(ex.Message);

            }

        }

Agregando nuestro middleware al pipeline de la aplicación

El siguiente paso una vez tenemos nuestro ExceptionMiddleware, es necesario agregarlo al pipeline de ejecucion de la aplicación, dado que necesitamos capturar cualquier excepción que ocurra en el pipeline, debemos agregar nuestro middleware para que sea ejecutado en primer lugar.

en la clase Startup del proyecto agregamos la siguiente linea en el metodo Configure

app.UseMiddleware<ExceptionMiddleware>();

La cual indica al pipelline que debe ejecutar nuestra clase ExceptionMiddleware en primer lugar en el pipeline de ejecución.


  public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            app.UseMiddleware<ExceptionMiddleware>();

            if (env.IsDevelopment())
            {                
                app.UseSwagger();
                app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "API v1"));
            }


            //  app.UseHttpsRedirection();
           app.UseRouting();
           app.UseCors("CorsPolicy");

            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }

De esta forma podremos tener un control de excepciones general para toda la aplicación ya que cualquier excepción generada por otros elementos del pipeline podra ser capturada por nuestro middleware.

De igual forma tambien podemos personalizar nuestras respuestas a los clientes que invoquen nuestro API con el fin de definir una forma estandar en el frontend para identificar los diferentes tipos de errores (500,400, 401, 404 etc.)