.NET › Global Exception Handling & Middleware

What is the difference between handling exceptions with middleware versus using try/catch blocks directly in controller actions?

Intermediate

Answer

Middleware-based global exception handling centralizes error-to-response translation logic in one place, applying consistently across every endpoint without needing to remember to add try/catch to each action -- reducing duplication and ensuring uniform error responses. Try/catch in individual actions is appropriate for handling specific, expected exceptions where the action needs to perform particular recovery logic or return a genuinely different response shape for that specific scenario, rather than the generic error handling the global middleware provides.
// Global middleware handles the general case -- no try/catch needed here
[HttpGet("{id}")]
public IActionResult GetProduct(int id) {
    var product = _service.GetById(id) ?? throw new NotFoundException();
    return Ok(product);
}

// Local try/catch for action-specific recovery logic
[HttpPost("import")]
public async Task<IActionResult> ImportProducts(IFormFile file) {
    try {
        await _importer.ImportAsync(file);
        return Ok();
    } catch (InvalidFileFormatException ex) {
        return BadRequest(new { ex.Message, SupportedFormats = new[] { "csv", "xlsx" } });  // action-specific response
    }
}
Real-world example A file import endpoint uses a local try/catch to return a highly specific, actionable error response (listing supported formats) for a particular exception type, while relying on the global handler for every other, more generic failure scenario throughout the rest of the API.

Common follow-ups: When does local try/catch handling risk creating inconsistent error response formats?;How do you decide which exceptions deserve action-specific handling versus the global default?