Types of Error Handling in Web API .NET 8 with Example

Error handling is a mechanism to take control of unexpected situations that occur in the system during processing and return any helpful error response from a NET 8 Web API. That said, here are some different ways to do error handling (with examples for each:)

1. Global Exception Handling with Middleware

You can write middleware to catch unhandled exceptions for your free application. This method is useful for consistently returning the same response in case of an error.

public class ExceptionHandlingMiddleware
{
    private readonly RequestDelegate _next;

    public ExceptionHandlingMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task Invoke(HttpContext context)
    {
        try
        {
            await _next(context);
        }
        catch (Exception ex)
        {
            await HandleExceptionAsync(context, ex);
        }
    }

    private static Task HandleExceptionAsync(HttpContext context, Exception exception)
    {
        context.Response.ContentType = "application/json";
        context.Response.StatusCode = StatusCodes.Status500InternalServerError;

        var response = new { message = "An error occurred.", detail = exception.Message };
        return context.Response.WriteAsJsonAsync(response);
    }
}

// Register in Program.cs

app.UseMiddleware<ExceptionHandlingMiddleware>();

2. Using ExceptionFilter in Controllers

Then we have ExceptionFilter, which is action-specific for that controller only.

public class CustomExceptionFilter : IExceptionFilter
{
    public void OnException(ExceptionContext context)
    {
        context.Result = new ObjectResult(new { message = "Controller-level error occurred." })
        {
            StatusCode = StatusCodes.Status500InternalServerError
        };
    }
}

// Apply the filter on a controller
[ServiceFilter(typeof(CustomExceptionFilter))]
[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
    [HttpGet]
    public IActionResult GetProducts()
    {
        throw new Exception("Something went wrong!");
    }
}

3. Handling Errors in Action Methods

You can also catch exceptions within action methods (using try-catch block) for more detailed error handling.

[HttpGet("{id}")]
public IActionResult GetProduct(int id)
{
    try
    {
        var product = _productService.GetProductById(id);
        if (product == null)
            return NotFound(new { message = "Product not found" });

        return Ok(product);
    }
    catch (Exception ex)
    {
        return StatusCode(500, new { message = "An error occurred", detail = ex.Message });
    }
}

4. Using Problem Details

Enriched problem details are a standardized way of returning error information in an enriched format (often as part of the HTTP 400 or 500 response).

[HttpGet("{id}")]
public IActionResult GetProduct(int id)
{
    if (id <= 0)
    {
        var problemDetails = new ProblemDetails
        {
            Status = StatusCodes.Status400BadRequest,
            Title = "Invalid Product ID",
            Detail = "The Product ID must be greater than zero."
        };
        return BadRequest(problemDetails);
    }

    // Continue with normal logic
}

5. Validation Error Handling

You can have global model validation errors, but you resolve them in a particular action. You can simply use [ApiController] and have automatic handling of model validation errors.

public class Product
{
    [Required]
    public string Name { get; set; }

    [Range(1, 1000)]
    public decimal Price { get; set; }
}

[HttpPost]
public IActionResult CreateProduct(Product product)
{
    // If model is invalid, ASP.NET automatically returns a 400 response
    return Ok(new { message = "Product created successfully" });
}

Unlike [ApiController], when the model is invalid,. NET 8 will automatically return an error response with the details of the validation failure with a 400 status code.

6. Using ExceptionHandler for Global Error Pages

In production, UseExceptionHandler allows you to use your own custom error pages.

app.UseExceptionHandler("/error");

app.Map("/error", (HttpContext context) =>
{
    var response = new { message = "An error occurred" };
    return Results.Problem(statusCode: StatusCodes.Status500InternalServerError, detail: response.message);
});

Summary

  • Middleware: Global exception handling across the app.
  • Exception Filter: Controller-level error handling.
  • Action-level try-catch: Fine-grained error handling within methods.
  • Problem Details: Standardized error response format.
  • Model Validation: Automatic 400 responses for invalid models.
  • Exception Handler Endpoint: Custom error page or response.