.NET 6.0 - Global Error Handler Tutorial with Example
Tutorial built with .NET 6.0
Other versions available:
- .NET: .NET 5.0, ASP.NET Core 3.1
- Next.js: Next.js 11
This is a quick post to show how to implement a global exception handler in .NET 6.0.
These are the main pieces involved in the error handling process that we'll cover in this tutorial:
- Global Error Handler Middleware - custom middleware that catches all exceptions and determines which HTTP response code to return based on the exception type.
- App Exception - a custom exception class to differentiate between handled exceptions thrown by application code and unhandled exceptions thrown by the .NET framework.
- Program.cs - the entry point to the .NET 6 app, it adds the global error handler middleware to the application request pipeline.
- Example Error Throwing Service - an example service that shows how to throw application exceptions that will be handled by the global error handler.
The below code snippets are taken from a .NET API tutorial I posted recently, for the full tutorial or to download and test the code locally see .NET 6.0 - User Registration and Login Tutorial with Example API.
.NET Global Error Handler Middleware
The global error handler is used catch all errors and remove the need for duplicated error handling code throughout the .NET api. It's configured as middleware in the configure HTTP request pipeline section of the Program.cs file.
Errors of type AppException
are treated as custom (app specific) errors that return a 400 Bad Request
response, the .NET built-in KeyNotFoundException
class is used to return 404 Not Found
responses, all other exceptions are unhandled and return a 500 Internal Server Error
response.
See the example service for examples of how to throw an AppException
or KeyNotFoundException
that will be handled by this error handler middleware.
namespace WebApi.Helpers;
using System.Net;
using System.Text.Json;
public class ErrorHandlerMiddleware
{
private readonly RequestDelegate _next;
public ErrorHandlerMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
try
{
await _next(context);
}
catch (Exception error)
{
var response = context.Response;
response.ContentType = "application/json";
switch(error)
{
case AppException e:
// custom application error
response.StatusCode = (int)HttpStatusCode.BadRequest;
break;
case KeyNotFoundException e:
// not found error
response.StatusCode = (int)HttpStatusCode.NotFound;
break;
default:
// unhandled error
response.StatusCode = (int)HttpStatusCode.InternalServerError;
break;
}
var result = JsonSerializer.Serialize(new { message = error?.Message });
await response.WriteAsync(result);
}
}
}
.NET App Exception
The app exception is a custom exception class used to differentiate between handled and unhandled exceptions in the .NET API. Handled exceptions are generated by application code and used to return friendly error messages, for example business logic or validation exceptions caused by invalid request parameters, whereas unhandled exceptions are generated by the .NET framework or caused by bugs in application code.
namespace WebApi.Helpers;
using System.Globalization;
// custom exception class for throwing application specific exceptions (e.g. for validation)
// that can be caught and handled within the application
public class AppException : Exception
{
public AppException() : base() {}
public AppException(string message) : base(message) { }
public AppException(string message, params object[] args)
: base(String.Format(CultureInfo.CurrentCulture, message, args))
{
}
}
.NET 6 Program
The .NET 6 Program file contains top-level statements which are converted by the new C# 10 compiler into a Main()
method and Program
class for the .NET program. The Main()
method is the entry point for a .NET application, when an app is started it searches for the Main()
method to begin execution. The top-level statements can be located anywhere in the project but are typically placed in the Program.cs
file, only one file can contain top-level statements within a .NET application.
The Program class configures .NET Dependency Injection (DI) the HTTP request pipeline for the application. The global error handler middleware is configured on line 51
by calling app.UseMiddleware<ErrorHandlerMiddleware>();
using Microsoft.EntityFrameworkCore;
using WebApi.Authorization;
using WebApi.Helpers;
using WebApi.Services;
var builder = WebApplication.CreateBuilder(args);
// add services to DI container
{
var services = builder.Services;
var env = builder.Environment;
// use sql server db in production and sqlite db in development
if (env.IsProduction())
services.AddDbContext<DataContext>();
else
services.AddDbContext<DataContext, SqliteDataContext>();
services.AddCors();
services.AddControllers();
// configure automapper with all automapper profiles from this assembly
services.AddAutoMapper(typeof(Program));
// configure strongly typed settings object
services.Configure<AppSettings>(builder.Configuration.GetSection("AppSettings"));
// configure DI for application services
services.AddScoped<IJwtUtils, JwtUtils>();
services.AddScoped<IUserService, UserService>();
}
var app = builder.Build();
// migrate any database changes on startup (includes initial db creation)
using (var scope = app.Services.CreateScope())
{
var dataContext = scope.ServiceProvider.GetRequiredService<DataContext>();
dataContext.Database.Migrate();
}
// configure HTTP request pipeline
{
// global cors policy
app.UseCors(x => x
.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader());
// global error handler
app.UseMiddleware<ErrorHandlerMiddleware>();
// custom jwt auth middleware
app.UseMiddleware<JwtMiddleware>();
app.MapControllers();
}
app.Run("http://localhost:4000");
.NET Example Error Throwing Service
This is an example service to show how to throw custom app exceptions and not found exceptions that will be handled by the global error handler middleware, resulting in 400
and 404
HTTP responses respectively.
namespace WebApi.Services;
using WebApi.Helpers;
public class ExampleErrorService
{
public void ExampleErrors() {
// a custom app exception that will return a 400 response
throw new AppException("Email or password is incorrect");
// a key not found exception that will return a 404 response
throw new KeyNotFoundException("Account not found");
}
}
Need Some .NET Help?
Search fiverr for freelance .NET developers.
Follow me for updates
When I'm not coding...
Me and Tina are on a motorcycle adventure around Australia.
Come along for the ride!