Previous ControllerBase vs Controller in ASP.NET Core REST-vs-GraphQL Next

Global Exception Handling in ASP.NET Core

Global Exception Handling in ASP.NET Core

Implementing global exception handling in ASP.NET Core is essential for building resilient and maintainable applications. You’ve got multiple strategies depending on your version and architecture preferences.

📌 Why Exception Handling Matters

Exception handling ensures that your application can gracefully recover from unexpected errors, provide meaningful feedback to users, and log issues for diagnostics. Global filters centralize this logic, making your code cleaner and more maintainable.

In ASP.NET Core, global exception handling is usually done via middleware so that any unhandled exceptions that bubble up the request pipeline are caught in one place. This ensures consistency (structured error responses, logging, etc.) instead of scattering try–catch blocks across controllers. Here are the main approaches:

1. Use Built-in Middleware UseExceptionHandler

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/error");
        app.UseHsts();
    }

    app.UseHttpsRedirection();
    app.UseRouting();
    app.UseAuthorization();

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

Error Controller:

[ApiController]
public class ErrorController : ControllerBase
{
    [Route("error")]
    public IActionResult HandleError() =>
        Problem();
}

2. Custom Middleware (Pre .NET 8)

For more control (logging, custom JSON format, etc.), create your own middleware:

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 Invoke(HttpContext context)
    {
        try
        {
            await _next(context);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Unhandled exception");

            context.Response.StatusCode = StatusCodes.Status500InternalServerError;
            context.Response.ContentType = "application/json";

            var response = new
            {
                error = "An unexpected error occurred",
                details = ex.Message
            };

            await context.Response.WriteAsJsonAsync(response);
        }
    }
}

Register it in the pipeline:

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

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

3. New in .NET 8 Use IExceptionFilter Abstraction

This is the modern, modular approach introduced in .NET 8.

public class GlobalExceptionFilter : IExceptionFilter
{
    private readonly ILogger<GlobalExceptionFilter> _logger;

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

    public void OnException(ExceptionContext context)
    {
        _logger.LogError(context.Exception, "Unhandled exception");

        context.Result = new ObjectResult(new
        {
            error = "An error occurred",
            details = context.Exception.Message

        })
        {
            StatusCode = StatusCodes.Status500InternalServerError
        };
        context.ExceptionHandled = true;
    }
}

Register Filter in Program.cs :

services.AddControllers(options =>
{
    options.Filters.Add<GlobalExceptionFilter>();
});

Key Points

  • Use UseDeveloperExceptionPage in development.
  • Use UseExceptionHandler or custom middleware in production.
  • Exception filters are useful for MVC/Web API, but middleware works across the whole pipeline.

✅ Best Practices

  • Use UseDeveloperExceptionPage in development for detailed errors.
  • Use UseExceptionHandler or custom middleware in production.
  • Log exceptions with context using ILogger.
  • Return standardized error responses using ProblemDetails.
  • Sanitize error messages to avoid exposing stack traces or sensitive info.

⚠️ Precautions

  • Don't catch exceptions silently—always log them.
  • Avoid leaking internal details in error responses.
  • Ensure filters and middleware don't conflict or duplicate logic.

🌟 Advantages

  • Centralized error handling improves maintainability.
  • Consistent error responses across the application.
  • Improved logging and diagnostics.

📉 Disadvantages

  • May obscure specific error context if over-generalized.
  • Requires careful configuration to avoid swallowing exceptions.

🧠 Pro Tips for Architects Like You

  • Centralize Logging: Inject ILogger<T> into your handler to log exceptions with context.
  • Use ProblemDetails: It’s the standard format for error responses in REST APIs.
  • Avoid exposing stack traces: In production—sanitize messages for security.
  • Integrate with Serilog or Application Insights: For richer telemetry.
Back to Index
Previous ControllerBase vs Controller in ASP.NET Core REST-vs-GraphQL Next
*