One of the most crucial components of creating reliable, safe, and production-ready apps is exception management. Errors such as faulty input, network failures, database connection problems, unhandled null values, or unauthorized access are eventually encountered by every application. The difficulty lies not in preventing these mistakes but in managing them consistently and with grace.


An extensive and potent exception-handling pipeline is offered by ASP.NET Core. Using real-world examples, global middleware, logging, structured API answers, custom exceptions, and recommended practices, this article covers exception handling from the fundamentals to sophisticated methods.

1. What is an Exception?
An exception is an unexpected event that occurs during program execution and disrupts the normal flow of the application.
Common examples include:

  • Trying to divide by zero
  • Attempting to read null values
  • Accessing invalid array indexes
  • Database connection failures
  • Invalid type conversions

Exceptions help developers identify problems, but unhandled exceptions cause application crashes. This is why exception handling is essential.

2. Why Exception Handling Is Important
Proper exception handling provides several benefits:

  • Prevents application crashes
  • Displays user-friendly messages
  • Prevents sensitive information from leaking
  • Produces reliable API responses
  • Helps developers diagnose issues through logs
  • Improves maintainability and robustness

Without exception handling, end users may see application crashes or confusing error pages. With proper handling, errors become predictable, controlled, and secure.

3. Types of Exceptions in .NET

System Exceptions
These are built-in .NET exception types, such as:

  • NullReferenceException
  • ArgumentException
  • InvalidOperationException
  • FormatException
  • IndexOutOfRangeException

Application Exceptions
Custom exceptions created by developers to handle logical or business-related issues:
public class InvalidOrderException : Exception
{
    public InvalidOrderException(string message) : base(message) { }
}

Business Rule Exceptions
These represent domain-level issues, such as:

  • Insufficient balance
  • Invalid order ID
  • Inactive account
  • Unauthorized operation

4. Try–Catch–Finally - Basic Exception Handling
The simplest form of exception handling uses try, catch, and finally blocks.
try
{
    int x = 10;
    int y = 0;
    int result = x / y;
}
catch (DivideByZeroException ex)
{
    Console.WriteLine("Cannot divide by zero.");
}
catch (Exception ex)
{
    Console.WriteLine("An unexpected error occurred.");
}
finally
{
    Console.WriteLine("This block always executes.");
}


The finally block is optional and is typically used to release resources like database connections or file handles.

5. Exception Handling in ASP.NET Core
ASP.NET Core offers several layers of exception handling:

  • Developer exception page
  • Production exception handler using UseExceptionHandler
  • Custom global exception handling middleware
  • Exception filters
  • Validation responses
  • Structured error responses (Problem Details)
  • Logging via ILogger or external log providers

Each technique has a specific use case depending on whether you are in development or production.

6. Developer Exception Page - Development Environment
This provides detailed error information, including:

  • Stack trace
  • Source file and line number
  • Error message
  • Request details

Enable it only in development:
if (app.Environment.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
}

Never enable this in production because it exposes sensitive details.

7. Built-in Production Exception Handler - UseExceptionHandler
This middleware catches unhandled exceptions globally.

Step 1: Enable the handler in Program.cs

app.UseExceptionHandler("/error");

Step 2: Create the error endpoint
app.Map("/error", (HttpContext context) =>
{
    return Results.Problem("An unexpected error occurred.");
});


This handles all unhandled exceptions without exposing internal details.

8. Global Exception Handling Middleware - Preferred Method

Creating a custom middleware provides full control and centralizes all exception handling logic.

ExceptionMiddleware.cs
using System.Net;
using System.Text.Json;

public class ExceptionMiddleware : IMiddleware
{
    public async Task InvokeAsync(HttpContext context, RequestDelegate next)
    {
        try
        {
            await next(context);
        }
        catch (Exception ex)
        {
            context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
            context.Response.ContentType = "application/json";

            var errorResponse = new
            {
                StatusCode = context.Response.StatusCode,
                Message = "An unexpected error occurred.",
                Detail = ex.Message
            };

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

Register in the Program.cs
builder.Services.AddTransient<ExceptionMiddleware>();
app.UseMiddleware<ExceptionMiddleware>();


This approach is suitable for production APIs.

9. Exception Filters - MVC Specific
Exception filters allow centralized handling for controller actions.

CustomExceptionFilter.cs
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;

public class CustomExceptionFilter : IExceptionFilter
{
    public void OnException(ExceptionContext context)
    {
        context.Result = new JsonResult(new
        {
            Message = "An error occurred.",
            Detail = context.Exception.Message
        })
        { StatusCode = 500 };
    }
}

Register in the Program.cs
builder.Services.AddControllers(options =>
{
    options.Filters.Add<CustomExceptionFilter>();
});


Filters are useful when you want exception handling only for controllers, not for middleware-level requests.

10. Validation Exception Handling
ASP.NET Core automatically validates DTOs using data annotations.

Example DTO:
public class RegisterDto
{
    [Required]
    public string Email { get; set; }

    [MinLength(6)]
    public string Password { get; set; }
}

When validation fails, ASP.NET Core returns a structured 400 response:
{
  "errors": {
    "Password": ["The field Password must be a string with a minimum length of 6."]
  }
}

No additional exception handling is required for validation errors.

11. Structured Error Responses Using ProblemDetails
ASP.NET Core supports the Problem Details format (RFC 7807).

Example:
return Problem(
    title: "Internal Server Error",
    detail: "Database connection failed",
    statusCode: 500
);

This produces a structured response:
{
  "title": "Internal Server Error",
  "status": 500,
  "detail": "Database connection failed"
}


Consistent error responses improve debugging and client-side error handling.

12. Logging Exceptions Using ILogger
Logging is critical for diagnosing issues and monitoring applications.
try
{
    int.Parse("abc");
}
catch (Exception ex)
{
    _logger.LogError(ex, "Error while parsing number");
}


Logging providers supported in ASP.NET Core:

  • Console logging
  • Debug logging
  • Serilog
  • NLog
  • Seq
  • Application Insights

Logs should be centralized in production.

13. Custom Business Exceptions
Applications often need domain-level error handling.
public class InsufficientFundsException : Exception
{
    public InsufficientFundsException(string message) : base(message) { }
}

Usage:
if (balance < amount)
    throw new InsufficientFundsException("Insufficient funds to complete transaction.");


Catch these globally and return user-friendly messages.

14. Real-World Example: Handling Exceptions in a Controller
[HttpGet("{id}")]
public async Task<IActionResult> GetOrder(int id)
{
    var order = await _context.Orders.FindAsync(id);

    if (order == null)
        throw new KeyNotFoundException("Order not found");

    return Ok(order);
}


Global middleware converts this into a structured error response without exposing sensitive details.

15. Best Practices for Exception Handling in Production

  • Never expose internal exception messages to users
  • Always log exceptions with proper context
  • Use global exception handling middleware
  • Standardize error responses with ProblemDetails
  • Use try-catch only where required
  • Validate user input before processing
  • Create custom exceptions for business logic
  • Return proper HTTP status codes
  • Separate domain, application, and infrastructure exceptions

Proper exception handling improves code quality, application reliability, and security. Thank you for reading this complete guide on Exception Handling in ASP.NET Core. Exception handling is a critical skill for building stable, secure, and maintainable applications. By implementing global handlers, logging, structured responses, and custom exception logic, you ensure your APIs remain predictable and professional under all circumstances.