ASP.NET Core  

How to Handle Global Exception Handling in ASP.NET Core Web API

Introduction

Handling errors properly is one of the most important parts of building a robust ASP.NET Core Web API. If exceptions are not handled correctly, your application may crash, expose sensitive data, or provide a poor user experience.

Global exception handling allows you to manage all errors in one place instead of writing try-catch blocks in every controller or service. This makes your code cleaner, easier to maintain, and more secure.

In this article, you will learn how to implement global exception handling in ASP.NET Core Web API step by step using simple language and real-world examples.

What is Global Exception Handling?

Global exception handling is a centralized way to catch and handle all runtime errors (exceptions) in your application.

Instead of writing this everywhere:

try
{
    // logic
}
catch(Exception ex)
{
    // handle error
}

You define a single place that handles all exceptions automatically.

Benefits:

  • Cleaner code (no repetitive try-catch)

  • Better error management

  • Consistent API responses

  • Improved security

Types of Exceptions in ASP.NET Core

Common types of exceptions you may encounter:

  • System exceptions (NullReferenceException, DivideByZeroException)

  • Custom exceptions (business logic errors)

  • Validation errors

  • Unauthorized access errors

Understanding these helps you handle them properly.

Approach 1: Using Built-in Exception Handling Middleware

ASP.NET Core provides a built-in middleware to handle global exceptions.

Step 1: Configure Exception Handling in Program.cs

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/error");
}

This redirects all exceptions to a specific endpoint.

Step 2: Create Error Controller

using Microsoft.AspNetCore.Diagnostics;
using Microsoft.AspNetCore.Mvc;

[ApiController]
public class ErrorController : ControllerBase
{
    [Route("/error")]
    public IActionResult HandleError()
    {
        var context = HttpContext.Features.Get<IExceptionHandlerFeature>();
        var exception = context?.Error;

        return Problem(
            detail: exception?.Message,
            title: "An error occurred",
            statusCode: 500
        );
    }
}

This ensures a consistent response format.

Approach 2: Custom Middleware for Global Exception Handling

This is the most recommended and flexible approach.

Step 1: Create Custom Middleware

Create a new file: ExceptionMiddleware.cs

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

public class ExceptionMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger<ExceptionMiddleware> _logger;

    public ExceptionMiddleware(RequestDelegate next, ILogger<ExceptionMiddleware> 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";
        context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;

        var response = new
        {
            StatusCode = context.Response.StatusCode,
            Message = "Something went wrong",
            Detailed = exception.Message
        };

        return context.Response.WriteAsync(JsonSerializer.Serialize(response));
    }
}

Step 2: Register Middleware in Program.cs

app.UseMiddleware<ExceptionMiddleware>();

Place it early in the pipeline (before other middlewares).

Handling Different Exception Types

You can customize responses based on exception types.

Example:

private static Task HandleExceptionAsync(HttpContext context, Exception exception)
{
    HttpStatusCode status;
    string message;

    switch (exception)
    {
        case UnauthorizedAccessException:
            status = HttpStatusCode.Unauthorized;
            message = "Unauthorized access";
            break;

        case ArgumentException:
            status = HttpStatusCode.BadRequest;
            message = "Invalid request";
            break;

        default:
            status = HttpStatusCode.InternalServerError;
            message = "Server error";
            break;
    }

    context.Response.StatusCode = (int)status;

    var result = JsonSerializer.Serialize(new
    {
        StatusCode = context.Response.StatusCode,
        Message = message
    });

    return context.Response.WriteAsync(result);
}

Creating Custom Exceptions

Custom exceptions help represent business logic errors.

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

Usage:

if (user == null)
{
    throw new NotFoundException("User not found");
}

Handle it in middleware for better responses.

Standard API Error Response Format

Use a consistent structure:

{
  "statusCode": 500,
  "message": "Error message",
  "details": "Optional details"
}

This improves frontend integration.

Logging Exceptions

Logging is critical for debugging and monitoring.

ASP.NET Core provides built-in logging:

_logger.LogError(exception, "Error occurred");

You can also integrate tools like:

  • Serilog

  • NLog

  • Application Insights

Best Practices for Global Exception Handling

  • Do not expose sensitive details in production

  • Use structured logging

  • Handle known exceptions separately

  • Return proper HTTP status codes

  • Keep response format consistent

Common Mistakes to Avoid

  • Using try-catch everywhere

  • Returning raw exception messages

  • Not logging errors

  • Incorrect status codes

Real-World Example Flow

  • Client sends request

  • API processes request

  • Exception occurs

  • Middleware catches exception

  • Logs error

  • Returns standardized JSON response

When to Use Global Exception Handling

Use it in:

  • REST APIs

  • Microservices

  • Enterprise applications

It ensures reliability and maintainability.

Summary

Global exception handling in ASP.NET Core Web API helps you manage all errors in a centralized way. By using built-in middleware or creating custom middleware, you can ensure consistent error responses, better logging, and improved application security. This approach reduces code duplication and makes your application more professional and production-ready.