Error Middleware
El ExceptionMiddleware
es un middleware de ASP.NET Core diseñado para interceptar y manejar excepciones que ocurren durante el procesamiento de una solicitud HTTP en una API REST. Su objetivo principal es proporcionar respuestas de error consistentes y formateadas en formato JSON, mejorando así la experiencia del cliente y facilitando la depuración y el manejo de errores.
using System.Net;using CodeDesignPlus.Net.Exceptions;using CodeDesignPlus.Net.Exceptions.Models;using CodeDesignPlus.Net.Serializers;using FluentValidation;using Microsoft.AspNetCore.Http;
namespace CodeDesignPlus.Net.Microservice.Commons.EntryPoints.Rest.Middlewares;
/// <summary>/// Middleware for handling exceptions in HTTP requests./// </summary>public class ExceptionMiddleware(RequestDelegate next){ /// <summary> /// Invokes the middleware to handle the HTTP context. /// </summary> /// <param name="context">The HTTP context.</param> /// <returns>A task representing the asynchronous operation.</returns> public async Task InvokeAsync(HttpContext context) { try { await next(context); } catch (ValidationException ex) { await HandleExceptionsAsync(context, ex); } catch (CodeDesignPlusException ex) { await HandleExceptionsAsync(context, ex); } catch (Exception ex) { await HandleExceptionsAsync(context, ex); } }
/// <summary> /// Handles CodeDesignPlus exceptions. /// </summary> /// <param name="context">The HTTP context.</param> /// <param name="exception">The CodeDesignPlus exception.</param> /// <returns>A task representing the asynchronous operation.</returns> private static Task HandleExceptionsAsync(HttpContext context, CodeDesignPlusException exception) { context.Response.ContentType = "application/json"; context.Response.StatusCode = (int)HttpStatusCode.BadRequest;
var response = new ErrorResponse(context.TraceIdentifier, exception.Layer);
response.AddError(exception.Code, exception.Message, null);
return context.Response.WriteAsync(JsonSerializer.Serialize(response)); }
/// <summary> /// Handles validation exceptions. /// </summary> /// <param name="context">The HTTP context.</param> /// <param name="exception">The validation exception.</param> /// <returns>A task representing the asynchronous operation.</returns> private static Task HandleExceptionsAsync(HttpContext context, ValidationException exception) { context.Response.ContentType = "application/json"; context.Response.StatusCode = (int)HttpStatusCode.BadRequest;
var errors = exception.Errors.Select(e => new ErrorDetail(e.ErrorCode, e.PropertyName, e.ErrorMessage));
var response = new ErrorResponse(context.TraceIdentifier, Layer.Application);
response.Errors.AddRange(errors);
return context.Response.WriteAsync(JsonSerializer.Serialize(response)); }
/// <summary> /// Handles general exceptions. /// </summary> /// <param name="context">The HTTP context.</param> /// <param name="exception">The general exception.</param> /// <returns>A task representing the asynchronous operation.</returns> private static Task HandleExceptionsAsync(HttpContext context, Exception exception) { context.Response.ContentType = "application/json"; context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
var result = new ErrorResponse(context.TraceIdentifier, Layer.None);
result.AddError("0-000", exception.Message, null);
return context.Response.WriteAsync(JsonSerializer.Serialize(result)); }}
¿Cómo Funciona?
El ExceptionMiddleware
se inserta en el pipeline de procesamiento de solicitudes HTTP de ASP.NET Core. Captura cualquier excepción que no sea manejada por los controladores de la API. Cuando una excepción ocurre, el middleware intercepta el flujo de ejecución, analiza el tipo de excepción y genera una respuesta HTTP con información detallada sobre el error. La respuesta se formatea en JSON, siguiendo una estructura común definida por la clase ErrorResponse
.
El middleware maneja los siguientes tipos de excepciones de forma específica:
ValidationException
(FluentValidation): Extrae los errores de validación del objeto de excepción y los incluye en la respuesta JSON, proporcionando detalles sobre cada campo que falló en la validación. La respuesta se envía con un código de estado HTTP 400 (Bad Request).CodeDesignPlusException
: Utiliza la información específica de este tipo de excepción (código, mensaje y capa de origen) para generar una respuesta JSON detallada, incluyendo el código de error personalizado. La respuesta se envía con un código de estado HTTP 400 (Bad Request).Exception
(genérica): Maneja cualquier otra excepción no capturada. Genera una respuesta JSON con un código de error genérico y un mensaje de error, indicando que ha ocurrido un error interno del servidor. La respuesta se envía con un código de estado HTTP 500 (Internal Server Error).
El middleware utiliza el JsonSerializer
de CodeDesignPlus.Net.Serializers
para serializar la respuesta en formato JSON. Además, el TraceIdentifier del contexto HTTP se incluye en la respuesta para facilitar el rastreo de errores.
Métodos
El ExceptionMiddleware
implementa los siguientes métodos y funciones principales:
InvokeAsync
Type: public async Task InvokeAsync(HttpContext context)
Método principal del middleware que se ejecuta para cada solicitud HTTP. Envuelve el resto del pipeline de procesamiento de solicitudes (delegado next
) en un bloque try-catch. En caso de que se produzca alguna excepción, delega el manejo a los métodos correspondientes (HandleExceptionsAsync
).
HandleExceptionsAsync (CodeDesignPlusException)
Type: private static Task HandleExceptionsAsync(HttpContext context, CodeDesignPlusException exception)
Maneja excepciones de tipo CodeDesignPlusException
. Establece el tipo de contenido de la respuesta como “application/json”, el código de estado HTTP como 400 (Bad Request) y construye un objeto ErrorResponse
a partir de la información de la excepción. Serializa la respuesta en formato JSON y la envía al cliente.
HandleExceptionsAsync (ValidationException)
Type: private static Task HandleExceptionsAsync(HttpContext context, ValidationException exception)
Maneja excepciones de tipo ValidationException
(de FluentValidation). Establece el tipo de contenido de la respuesta como “application/json”, el código de estado HTTP como 400 (Bad Request) y construye un objeto ErrorResponse
a partir de los errores de validación. Serializa la respuesta en formato JSON y la envía al cliente.
HandleExceptionsAsync (Exception)
Type: private static Task HandleExceptionsAsync(HttpContext context, Exception exception)
Maneja excepciones de tipo genérico (Exception
). Establece el tipo de contenido de la respuesta como “application/json”, el código de estado HTTP como 500 (Internal Server Error) y construye un objeto ErrorResponse
con un código de error genérico y un mensaje de error. Serializa la respuesta en formato JSON y la envía al cliente.
Ejemplo de Uso
Imagina que tienes un controlador de API que recibe un objeto de tipo CreateUserRequest
y que dicho objeto debe ser validado usando FluentValidation
.
// Ejemplo de un validadorusing FluentValidation;
public class CreateUserRequestValidator : AbstractValidator<CreateUserRequest>{ public CreateUserRequestValidator() { RuleFor(x => x.Name).NotEmpty().WithMessage("Name is required."); RuleFor(x => x.Email).EmailAddress().WithMessage("Email is not valid."); }}
// Ejemplo de un controladorusing Microsoft.AspNetCore.Mvc;using FluentValidation;using System.Net;
[ApiController][Route("[controller]")]public class UsersController : ControllerBase{ private readonly IValidator<CreateUserRequest> _validator; public UsersController(IValidator<CreateUserRequest> validator){ _validator = validator; }
[HttpPost] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest)] public IActionResult CreateUser([FromBody]CreateUserRequest request) { // Validamos el request var validationResult = _validator.Validate(request);
if(!validationResult.IsValid){ throw new ValidationException(validationResult.Errors); }
// Lógica de negocio ... return Ok(); }}
Si el CreateUserRequest
es invalido, el ExceptionMiddleware
capturara la excepcion de tipo ValidationException
y retornara una respuesta con el estado BadRequest
junto con el detalle de los errores de validación en formato JSON.
Conclusiones
El ExceptionMiddleware
es un componente esencial para construir APIs REST robustas y fáciles de usar. Centraliza el manejo de excepciones, garantiza respuestas consistentes y detalladas para los clientes y simplifica la depuración de errores. Al interceptar y manejar excepciones de validación, excepciones personalizadas y excepciones generales, el middleware contribuye significativamente a la estabilidad y la calidad de la API.