Globally Exception Handling using Exception Filters vs Middleware
Tags: dotnet, exceptionhandling, programming, csharp
Published: 2023-08-12 07:54:03

In this blog post, I want to talk about using `IExceptionFilter`(IAsyncExceptionFilter) for globally handling exceptions in ASP.NET Core applications and compare it to using middleware for the same purpose.

Exception filters are classes that implement the `IExceptionFilter` interface and can be applied to controllers or actions using the `[ExceptionFilter]` attribute or you can register them globally in `Program.cs`. They provide a way to handle exceptions that occur **during the execution of an action method** and can perform custom logic such as logging, displaying error messages, or redirecting to another page.

Middleware are components that are invoked in a pipeline between the request and the response. They can also handle exceptions that occur in the application by using the `app.UseExceptionHandler()` extension method in the Startup class. They can perform similar tasks as exception filters, but they have access to the **entire HTTP context, not just the action context**.

One of the main differences between exception filters and middleware is that exception filters are executed **after the action method**, while middleware is executed **before and after the action method**. This means that exception filters can only handle exceptions that occur **inside the action method**, while middleware can handle exceptions that occur **anywhere in the pipeline**, including in other middleware components or in the framework itself.

Another difference is that exception filters are more granular and can be applied selectively to specific controllers or actions, while middleware is more global and apply to all requests. This means that exception filters can provide more fine-grained control over how to handle different types of exceptions, while middleware can provide a consistent way to handle all exceptions in the application.

Here is some sample code that shows how to use exception filters and middleware for handling exceptions:

For instance, I have created an error record like:

```csharp
public sealed record Error(string StatusCode, string Message);
```

and then I implemented `IExceptionFilter` in the `GlobalExceptionFilter` class:

```csharp
public class GlobalExceptionFilter : IExceptionFilter
{
    private readonly IHostEnvironment _hostEnvironment;

    public GlobalExceptionFilter(IHostEnvironment hostEnvironment)
    {
        _hostEnvironment = hostEnvironment;
    }

    public void OnException(ExceptionContext context)
    {
        if (!_hostEnvironment.IsDevelopment())
        {
            var error = new Error("500", context.Exception.Message);

            context.Result = new JsonResult(error)
            {
                StatusCode = (int)HttpStatusCode.InternalServerError
            };
        }
        else
        {
            context.Result = new ContentResult
            {
                Content = context.Exception.ToString()
            };
        }            
    }
}
```

**Tip:** `context.Result` accept `IActionResult` so you have plenty of options here, for instance, you can write `new RedirectToPageResult("/Error")` and redirect users to a generic error page etc.

Now the only remaining work is to register this class in the `Program.cs` file:

```csharp
builder.Services
.AddControllers(c => c.Filters.Add(typeof(GlobalExceptionFilter)));
```

## Summery

- Middleware runs before and after the action execution pipeline, so it cannot access the action context or the model state.
- `IExceptionFilter` runs inside the action execution pipeline, so it has access to the action context and the model state, and it can change the response status code and headers before they are written to the response stream.
- Exception filters are good for trapping exceptions that occur within actions.
- Exception filters are not as flexible as error-handling middleware