Isaac.

architecture

Global Exception Handling

Implement centralized error handling in your application.

By Emem IsaacApril 11, 20232 min read
#exception handling#middleware#errors#logging
Share:

A Simple Analogy

Global exception handling is like a safety net. Any errors fall through and are caught at the bottom.


Why Global Handling?

  • Consistency: Same response format everywhere
  • Logging: Centralized error tracking
  • Security: Don't expose stack traces
  • UX: User-friendly error messages
  • Maintenance: One place to fix error handling

Middleware Approach

public class ExceptionHandlingMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger<ExceptionHandlingMiddleware> _logger;
    
    public ExceptionHandlingMiddleware(RequestDelegate next, ILogger<ExceptionHandlingMiddleware> logger)
    {
        _next = next;
        _logger = logger;
    }
    
    public async Task InvokeAsync(HttpContext context)
    {
        try
        {
            await _next(context);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Unhandled exception");
            await HandleExceptionAsync(context, ex);
        }
    }
    
    private static Task HandleExceptionAsync(HttpContext context, Exception exception)
    {
        context.Response.ContentType = "application/json";
        
        var response = exception switch
        {
            ValidationException ve => new ErrorResponse
            {
                StatusCode = StatusCodes.Status400BadRequest,
                Message = "Validation failed",
                Errors = ve.Errors
            },
            NotFoundException ne => new ErrorResponse
            {
                StatusCode = StatusCodes.Status404NotFound,
                Message = ne.Message
            },
            _ => new ErrorResponse
            {
                StatusCode = StatusCodes.Status500InternalServerError,
                Message = "An unexpected error occurred"
            }
        };
        
        context.Response.StatusCode = response.StatusCode;
        return context.Response.WriteAsJsonAsync(response);
    }
}

// Register
app.UseMiddleware<ExceptionHandlingMiddleware>();

Custom Exception Classes

public class ValidationException : Exception
{
    public List<string> Errors { get; }
    
    public ValidationException(List<string> errors)
    {
        Errors = errors;
    }
}

public class NotFoundException : Exception
{
    public NotFoundException(string message) : base(message) { }
}

public class UnauthorizedException : Exception
{
    public UnauthorizedException(string message) : base(message) { }
}

Error Response Format

public class ErrorResponse
{
    public int StatusCode { get; set; }
    public string Message { get; set; }
    public List<string> Errors { get; set; }
    public string TraceId { get; set; }
}

// Usage
var errorResponse = new ErrorResponse
{
    StatusCode = 400,
    Message = "Invalid request",
    Errors = new[] { "Name is required", "Email is invalid" },
    TraceId = context.TraceIdentifier
};

Best Practices

  1. Specific exceptions: Create custom exception types
  2. Logging: Always log exceptions
  3. Status codes: Use appropriate HTTP codes
  4. Security: Don't expose sensitive details
  5. Consistency: Same format for all errors

Related Concepts

  • Try-catch blocks
  • Error monitoring
  • Logging frameworks
  • Recovery strategies

Summary

Implement global exception handling with middleware to catch errors, log them, and return consistent responses.

Share:

Written by Emem Isaac

Expert Software Engineer with 15+ years of experience building scalable enterprise applications. Specialized in ASP.NET Core, Azure, Docker, and modern web development. Passionate about sharing knowledge and helping developers grow.

Ready to Build Something Amazing?

Let's discuss your project and explore how my expertise can help you achieve your goals. Free consultation available.

💼 Trusted by 50+ companies worldwide | ⚡ Average response time: 24 hours