ASP.NET Core Web API
Lesson 16 of 35 46% of course

Global Exception Handling Middleware

16 · 8 min · 5/23/2026

Sign in to track progress and bookmarks.

Global Exception Handling Middleware

Wrapping every single Controller action in a try { ... } catch { return StatusCode(500); } block is an egregious anti-pattern. Not only does it bloat your codebase with redundant code, but it also risks missing errors thrown outside controllers. The architect's solution is a Global Exception Handling Middleware.

1. The Concept of Middleware

Middleware is a pipeline. Every HTTP request enters the pipeline, passes through various middlewares (like Authentication), hits the Controller, and the Response bubbles back out through the identical pipeline. If we put a massive try-catch at the very entrance of the pipeline, it will perfectly catch any exception thrown anywhere in the entire application.

2. Creating the Custom Middleware

We create a class that intercepts the HTTP context, invokes the rest of the application (await _next(context)), and catches any resulting explosions.

using System.Net;
using System.Text.Json;

public class GlobalExceptionMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger<GlobalExceptionMiddleware> _logger;
    private readonly IHostEnvironment _env;

    // _next represents the rest of the application pipeline
    public GlobalExceptionMiddleware(RequestDelegate next, ILogger<GlobalExceptionMiddleware> logger, IHostEnvironment env)
    {
        _next = next;
        _logger = logger;
        _env = env;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        try
        {
            // PROCEED: Let the request traverse the rest of the API
            await _next(context);
        }
        catch (Exception ex)
        {
            // BOOM! An unhandled exception was thrown somewhere!
            _logger.LogError(ex, "An unhandled exception occurred.");
            await HandleExceptionAsync(context, ex);
        }
    }

    private async Task HandleExceptionAsync(HttpContext context, Exception exception)
    {
        // Standardize the API response to always be JSON
        context.Response.ContentType = "application/json";
        context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;

        // Custom Error Object (If in Production, NEVER show the stack trace)
        var response = new 
        {
            StatusCode = context.Response.StatusCode,
            Message = _env.IsDevelopment() ? exception.Message : "An unexpected internal server error occurred.",
            Details = _env.IsDevelopment() ? exception.StackTrace : null
        };

        var json = JsonSerializer.Serialize(response);
        await context.Response.WriteAsync(json);
    }
}

3. Wiring it up in Program.cs

Location in the pipeline is critical. The exception handler MUST be the very first middleware registered so it completely wraps everything below it.

var app = builder.Build();

// 1. MUST BE FIRST IN THE PIPELINE
app.UseMiddleware<GlobalExceptionMiddleware>();

app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();

app.Run();

4. The Modern .NET 8 Alternative: IExceptionHandler

In newer .NET versions, Microsoft introduced an interface-based approach that is slightly cleaner than writing raw middleware.

public class CustomExceptionHandler : IExceptionHandler
{
    public async ValueTask<bool> TryHandleAsync(HttpContext context, Exception exception, CancellationToken cancellationToken)
    {
        // Log it, modify the response, and return true to indicate the exception was handled.
        context.Response.StatusCode = 500;
        await context.Response.WriteAsJsonAsync(new { Error = "Fatal Server Error." }, cancellationToken);
        return true; 
    }
}

// In Program.cs
builder.Services.AddExceptionHandler<CustomExceptionHandler>();
app.UseExceptionHandler(_ => { }); // Activates it in the pipeline

5. Interview Mastery

Q: "If an unhandled exception occurs in a background task (IHostedService), will the Global Exception Handling Middleware catch it?"

Architect Answer: "No. Middleware only exists within the context of an executing HTTP Request pipeline. An `IHostedService` (like a background timer running every 5 minutes) executes entirely outside of the HTTP pipeline. If an unhandled exception occurs inside a background service, the Middleware will never see it. Instead, you must wrap your background task execution logic in its own `try/catch` block. Furthermore, starting in .NET 6, unhandled exceptions in background services will violently crash the entire host process by default (terminating the web server completely) unless the `HostOptions.BackgroundServiceExceptionBehavior` is explicitly configured to Ignore them."

Test your knowledge

Quizzes linked to this course—pass to earn certificates.

Browse all quizzes
ASP.NET Core Web API

On this page

1. The Concept of Middleware 2. Creating the Custom Middleware 3. Wiring it up in Program.cs 4. The Modern .NET 8 Alternative: IExceptionHandler 5. Interview Mastery
1. Fundamentals & HTTP
Introduction to ASP.NET Core Web API REST Principles and HTTP Methods Controllers & ControllerBase Routing (Attribute vs Conventional) Action Return Types (IActionResult)
2. Request Handling
Model Binding (FromQuery, FromBody, FromRoute) Dependency Injection (DI) Deep Dive App Settings & The Options Pattern
3. Data Access & Architecture
EF Core Setup in Web API DbContext & Migrations Repository & Unit of Work Pattern Asynchronous Programming (async/await)
4. Data Transfer & Validation
Data Transfer Objects (DTOs) & AutoMapper Model Validation (DataAnnotations) FluentValidation Integration
5. Advanced Concepts
Global Exception Handling Middleware Content Negotiation (JSON vs XML) Pagination & Filtering Advanced Searching & Sorting HATEOAS (Hypermedia) Implementation Output Caching & Response Caching
6. Security & Authorization
Cross-Origin Resource Sharing (CORS) JWT Authentication Setup Access Tokens & Refresh Tokens Workflow Role-Based & Policy-Based Authorization API Key Authentication Rate Limiting & Throttling
7. Documentation & Testing
Swagger & OpenAPI Configuration Customizing API Documentation Unit Testing Controllers (xUnit & Moq) Integration Testing (WebApplicationFactory)
8. Microservices & Deployment
Consuming External APIs (IHttpClientFactory) Health Checks & Diagnostics API Versioning Strategies Deploying APIs (Docker & Azure)