ASP.NET Web API Global Exception Handling using Middleware and Filters Step by step Implementation and Top 10 Questions and Answers
 Last Update: April 01, 2025      17 mins read      Difficulty-Level: beginner

ASP.NET Web API Global Exception Handling Using Middleware and Filters

Exception handling is a critical aspect of building robust APIs that can gracefully manage and respond to errors that occur during runtime. In ASP.NET Web API, there are multiple strategies to handle exceptions globally, including the use of middleware and filters. Both approaches offer unique advantages, and understanding them is essential for developing resilient web APIs.

Introduction to Exception Handling in ASP.NET Web API

Before diving into specific strategies for global exception handling, let's briefly understand why it's important. An API without proper exception handling might return uninformative error messages to clients, making debugging difficult and affecting user experience. Global exception handling ensures that all exceptions are trapped and handled uniformly, leading to more predictable and maintainable code.

Using Middleware for Global Exception Handling

Middleware is one of the most powerful ways to handle exceptions in ASP.NET Web APIs. Middleware components sit in the HTTP request pipeline and can be configured to intercept requests and responses, including unhandled exceptions.

Setting Up Middleware

To set up middleware for exception handling, you need to create a custom middleware class. Here's a step-by-step guide:

  1. Create the Middleware Class

    public class ExceptionHandlingMiddleware
    {
        private readonly RequestDelegate _next;
    
        public ExceptionHandlingMiddleware(RequestDelegate next)
        {
            _next = next;
        }
    
        public async Task InvokeAsync(HttpContext context)
        {
            try
            {
                await _next(context);
            }
            catch (Exception ex)
            {
                await HandleExceptionAsync(context, ex);
            }
        }
    
        private static Task HandleExceptionAsync(HttpContext context, Exception exception)
        {
            // Log the exception here (optional)
            Console.WriteLine($"Something went wrong: {exception}");
    
            // Set the response code and content
            context.Response.ContentType = "application/json";
            context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
    
            return context.Response.WriteAsync(new
            {
                StatusCode = context.Response.StatusCode,
                ErrorMessage = "An unexpected error occurred, please try again later."
            }.ToString());
        }
    }
    
  2. Register the Middleware

    Add your middleware in the Configure method of the Startup.cs class.

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
    
        app.UseMiddleware<ExceptionHandlingMiddleware>();
    
        app.UseRouting();
    
        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
        });
    }
    
Advantages of Middleware
  • Centralized Exception Handling: Middleware allows you to handle exceptions in a single place, making it easier to maintain and modify.
  • Early in the Pipeline: Middleware can catch exceptions very early in the HTTP request pipeline, which is valuable for handling exceptions that occur before reaching controllers.
  • Granular Control: You can fine-tune the behavior of your exception handling, including logging, error messages, and more.

Using Filters for Global Exception Handling

ASP.NET Web API provides several filtering mechanisms for exception handling, including exception filters, action filters, and authorization filters. Here, we will focus on exception filters, which are specifically designed to handle exceptions thrown during the execution of a request.

Setting Up Exception Filters

To implement global exception handling using filters, you need to create a custom exception filter attribute.

  1. Create the Exception Filter

    public class GlobalExceptionFilter : IExceptionFilter
    {
        private readonly ILogger _logger;
    
        public GlobalExceptionFilter(ILogger<GlobalExceptionFilter> logger)
        {
            _logger = logger;
        }
    
        public void OnException(ExceptionContext context)
        {
            // Log the exception
            _logger.LogError($"Something went wrong: {context.Exception}");
    
            // Set the response code and content
            context.HttpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
            context.Result = new JsonResult(new
            {
                StatusCode = context.HttpContext.Response.StatusCode,
                ErrorMessage = "An unexpected error occurred, please try again later."
            });
        }
    }
    
  2. Register the Exception Filter

    Add your exception filter globally in the ConfigureServices method of the Startup.cs class.

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllers(options =>
        {
            options.Filters.Add<GlobalExceptionFilter>();
        });
    }
    
Advantages of Exception Filters
  • Integrated with MVC: Exception filters are tightly integrated with the MVC framework, making it easier to access MVC-specific features.
  • Context-Specific: You have access to ExceptionContext, which provides more context about the execution pipeline, such as the action descriptor, controller, and more.
  • Specific to Controllers: Exception filters are executed after action and authorization filters, which means they are not triggered for exceptions that occur during route resolution or middleware execution.

Combining Middleware and Filters

In practice, you can combine middleware and filters for a more comprehensive approach to exception handling. Middleware can catch and log exceptions very early in the pipeline, whereas filters provide more context and capabilities specific to the MVC framework.

Example

Here's a combined example where middleware handles exceptions early in the pipeline, and the filter handles exceptions that occur within the MVC framework.

  1. Create Middleware and Filter

    Use the middleware and filter classes defined earlier.

  2. Register Middleware and Filter

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
    
        app.UseMiddleware<ExceptionHandlingMiddleware>();
    
        app.UseRouting();
    
        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
        });
    }
    
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllers(options =>
        {
            options.Filters.Add<GlobalExceptionFilter>();
        });
    }
    

By combining middleware and filters, you can capture and handle exceptions at different stages of the request pipeline, providing a robust and flexible approach to exception handling.

Conclusion

Global exception handling is essential for building reliable and user-friendly APIs. In ASP.NET Web API, you can achieve this using middleware and filters. Middleware provides centralized and early exception handling, while filters offer more context and MVC-specific features. Combining both approaches gives you a powerful toolset for managing exceptions in your web APIs.

ASP.NET Web API Global Exception Handling Using Middleware and Filters: A Step-by-Step Guide for Beginners

Building an ASP.NET Web API application often involves handling exceptions gracefully. When an unexpected error occurs, you want to ensure your application responds in a predictable way, providing meaningful error messages to the client and logging the error for later analysis.

ASP.NET Core Web APIs provide two primary mechanisms for handling exceptions globally: Middleware and Action Filters. In this guide, we'll walk through setting up global exception handling using both methods to ensure your API is robust and error-tolerant.

Step-by-Step Guide for Beginners

Step 1: Set Up Your ASP.NET Web API Project

First, you need to create a new ASP.NET Core Web API project. You can do this via Visual Studio or the command line. Here’s how you can set it up:

Visual Studio:

  1. Open Visual Studio and create a new project.
  2. Select "ASP.NET Core Web API" from the list of templates.
  3. Configure your project settings and click "Create."
  4. Visual Studio sets up the default project structure.

Command Line:

  1. Open a terminal or command prompt.
  2. Run dotnet new webapi -n WebApiExceptionHandling.
  3. Navigate into the project directory using cd WebApiExceptionHandling.

Step 2: Create a Sample Controller

For demonstration, we'll create a simple controller that can throw an exception.

using Microsoft.AspNetCore.Mvc;

[ApiController]
[Route("[controller]")]
public class SampleController : ControllerBase
{
    [HttpGet]
    public IActionResult Get(int id)
    {
        if (id == 0)
        {
            throw new InvalidOperationException("Id cannot be zero.");
        }

        return Ok(new { Message = $"Id is: {id}" });
    }
}

Step 3: Implement Exception Handling Middleware

Middleware in ASP.NET Core is software that's assembled into an application pipeline to handle requests and responses. Let’s create a custom middleware to handle exceptions globally.

  1. Create a new class named ExceptionHandlerMiddleware:
using Microsoft.AspNetCore.Http;
using System.Net;
using System.Threading.Tasks;

public class ExceptionHandlerMiddleware
{
    private readonly RequestDelegate _next;

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

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

    private static Task HandleExceptionAsync(HttpContext context, Exception exception)
    {
        // Log the exception here (e.g., using Serilog, NLog, etc.)
        context.Response.ContentType = "application/json";
        context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;

        return context.Response.WriteAsync(new
        {
            StatusCode = context.Response.StatusCode,
            Message = "Internal Server Error from the custom middleware。",
            ExceptionMessage = exception.Message,
            ExceptionType = exception.GetType().Name
        }.ToString());
    }
}
  1. Add the middleware to the application pipeline in Startup.cs or Program.cs depending on your ASP.NET Core version.

For ASP.NET Core 3.x and later (Program.cs):

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.UseMiddleware<ExceptionHandlerMiddleware>();

app.MapControllers();

app.Run();

For ASP.NET Core 2.x and earlier (Startup.cs):

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    app.UseMiddleware<ExceptionHandlerMiddleware>();

    app.UseMvc();
}

Step 4: Implement Action Filters for Specific Scenarios

While Middleware provides a global way to handle exceptions, sometimes you might need more granular control. Action Filters allow you to handle exceptions at the action level.

  1. Create a custom action filter by inheriting from ExceptionFilterAttribute:
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc;
using System.Net;

public class CustomExceptionFilter : ExceptionFilterAttribute
{
    public override void OnException(ExceptionContext context)
    {
        if (context.ExceptionHandled)
        {
            return; // Skip if another filter already handled the exception
        }

        // Log the exception here (e.g., using Serilog, NLog, etc.)

        context.HttpContext.Response.ContentType = "application/json";
        context.HttpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError;

        context.Result = new JsonResult(new
        {
            StatusCode = context.HttpContext.Response.StatusCode,
            Message = "Internal Server Error from the custom filter.",
            ExceptionMessage = context.Exception.Message,
            ExceptionType = context.Exception.GetType().Name
        });

        context.ExceptionHandled = true;
    }
}
  1. Apply the custom action filter globally, or use [CustomExceptionFilter] attribute on specific controllers or actions:

Global Registration (Startup.cs/Program.cs):

builder.Services.AddControllers(options =>
{
    options.Filters.Add<CustomExceptionFilter>();
});
  1. Alternatively, apply it on a per-controller basis:
[ApiController]
[Route("[controller]")]
[CustomExceptionFilter]
public class SampleController : ControllerBase
{
    [HttpGet]
    public IActionResult Get(int id)
    {
        if (id == 0)
        {
            throw new InvalidOperationException("Id cannot be zero.");
        }

        return Ok(new { Message = $"Id is: {id}" });
    }
}

Step 5: Testing Your Implementation

Ensure your middleware and filter are working correctly by testing your API endpoints. You can use tools like Postman or Swagger UI.

  1. Run your application.
  2. Use Postman or a similar tool to send requests to the /Sample endpoint with different id values.
  3. Test with id as zero to trigger an exception. Ensure the global middleware or action filter responds with a formatted error message.

Summary

In this guide, we covered how to handle exceptions globally in an ASP.NET Web API application using both Middleware and Action Filters. Middleware provides a robust way to handle exceptions across the entire application, while Action Filters give you more granular control for specific controllers or actions.

By implementing these practices, you make your web APIs more robust and user-friendly by providing consistent error responses and valuable logging data for debugging.

Happy coding!

Top 10 Questions and Answers: ASP.NET Web API Global Exception Handling using Middleware and Filters

1. What is ASP.NET Web API Global Exception Handling?

Answer: ASP.NET Web API Global Exception Handling refers to the process of catching unhandled exceptions across the entire Web API application rather than handling them on a per-controller basis. This is crucial for providing a consistent error response to clients, logging errors, and ensuring that the application remains robust even when unexpected errors occur.

2. Why Should You Implement Global Exception Handling in ASP.NET Web API?

Answer: Implementing global exception handling in ASP.NET Web API is important for several reasons:

  • User Experience: Ensures that users receive user-friendly error messages.
  • Security: Prevents sensitive information, such as stack traces, from being exposed.
  • Logging: Centralizes logging of errors, facilitating easier debugging and maintenance.
  • Maintainability: Simplifies the management of exception handling logic across multiple controllers or actions.

3. What Are Middleware and Filters in ASP.NET Web API?

Answer: Middleware and filters are both used to handle requests and responses in ASP.NET Web API, but they operate at different levels:

  • Middleware: Operates at a lower level, allowing interception of HTTP requests and responses at a pipeline stage before reaching the controllers. Middleware components can handle requests, generate responses, and modify request and response data.
  • Filters: Are applied at a higher level within the MVC pipeline, after a controller has been selected but before the action method is executed. Filters can affect the execution flow of action methods, modify model data, perform validation, and handle exceptions.

4. How Can You Implement Global Exception Handling Using Middleware in ASP.NET Web API?

Answer: Implementing global exception handling using middleware involves creating a custom middleware component that can catch and process exceptions globally. Here’s a basic example:

public class ExceptionHandlingMiddleware
{
    private readonly RequestDelegate _next;

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

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

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

        return context.Response.WriteAsync(new
        {
            StatusCode = 500,
            Message = ex.Message
        }.ToString());
    }
}

// In Startup.cs, add the middleware to the request pipeline
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    app.UseMiddleware<ExceptionHandlingMiddleware>();
    
    // Other middleware registrations
}

5. How Can You Implement Global Exception Handling Using Filters in ASP.NET Web API?

Answer: Global exception handling using filters involves creating a custom attribute that inherits from ExceptionFilterAttribute and then registering it as a global filter.

public class GlobalExceptionFilter : ExceptionFilterAttribute
{
    public override void OnException(ExceptionContext context)
    {
        // Log the exception
        // ...

        var response = context.HttpContext.Response;
        response.ContentType = "application/json";
        response.StatusCode = StatusCodes.Status500InternalServerError;

        var result = new ObjectResult(new
        {
            StatusCode = 500,
            Message = context.Exception.Message
        });

        result.ContentTypes.Add(new MediaTypeHeaderValue("application/json"));

        context.Result = result;
    }
}

// In Startup.cs, register the global filter
public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers(options =>
    {
        options.Filters.Add<GlobalExceptionFilter>();
    });

    // Other service registrations
}

6. What Are the Advantages of Using Middleware for Exception Handling?

Answer: The advantages of using middleware for exception handling are:

  • Early Interception: Can catch exceptions early in the pipeline before reaching controllers.
  • Flexibility: Easier to manipulate HTTP contexts and responses.
  • Simplicity: Provides a straightforward way to handle exceptions globally without modifying existing controller logic.

7. What Are the Advantages of Using Filters for Exception Handling?

Answer: The advantages of using filters for exception handling are:

  • Granularity: Filters can provide more granular control over exception handling (e.g., different handling logic for different action methods or controllers).
  • Integration: Better integration with controller and action methods, making it easier to access method-specific metadata.
  • Lifecycle Events: Filters can tap into various lifecycle events, such as on action execution, on result execution, etc., providing flexibility in exception handling strategies.

8. Can Both Middleware and Filters Be Used Together for Exception Handling in ASP.NET Web API?

Answer: Yes, both middleware and filters can be used together for exception handling. This allows you to leverage the strengths of each approach:

  • Middleware: For broad, pipeline-level exception handling.
  • Filters: For fine-grained, controller/action-specific exception handling.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    app.UseMiddleware<ExceptionHandlingMiddleware>();
    
    // Other middleware registrations

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllers();
    });
}

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers(options =>
    {
        options.Filters.Add<GlobalExceptionFilter>();
    });

    // Other service registrations
}

9. How Can You Log Exceptions in Your Global Exception Handler?

Answer: Logging exceptions in a global exception handler is crucial for identifying and resolving issues. You can integrate with logging frameworks like Serilog, NLog, or the built-in logging abstraction provided by ASP.NET Core.

public class ExceptionHandlingMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger _logger;

    public ExceptionHandlingMiddleware(RequestDelegate next, ILogger<ExceptionHandlingMiddleware> logger)
    {
        _next = next;
        _logger = logger;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        try
        {
            await _next(context);
        }
        catch (Exception ex)
        {
            _logger.LogError($"Unhandled exception: {ex}");
            await HandleExceptionAsync(context, ex);
        }
    }

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

        return context.Response.WriteAsync(new
        {
            StatusCode = 500,
            Message = "An error occurred while processing your request."
        }.ToString());
    }
}

10. How Can You Customize the Error Response Format in Your Global Exception Handler?

Answer: Customizing the error response format allows you to provide more detailed or less-sensitive information to clients. Here’s an example of how you might construct a custom error response:

private static Task HandleExceptionAsync(HttpContext context, Exception ex)
{
    var response = new
    {
        StatusCode = StatusCodes.Status500InternalServerError,
        Message = "An error occurred while processing your request.",
        Details = ex.Message, // or more detailed information as needed
        Timestamp = DateTime.UtcNow
    };

    context.Response.ContentType = "application/json";
    context.Response.StatusCode = StatusCodes.Status500InternalServerError;

    return context.Response.WriteAsJsonAsync(response);
}

By leveraging both middleware and filters, you can implement a robust and flexible global exception handling strategy in your ASP.NET Web API application. This ensures that your application remains resilient and provides a consistent, user-friendly experience even in the face of unexpected errors.