ASP.NET Core 3.1 - Global Error Handler Tutorial
Built with ASP.NET Core 3.1
Other versions available:
- .NET: .NET 6.0, 5.0
- Next.js: Next.js 11
This is a quick post to show how to implement a global exception handler in ASP.NET Core 3.1.
These are the main pieces involved in the error handling process with a brief description of each:
- Global Error Handler Middleware - custom middleware that catches and handles all exceptions, and determines which HTTP response code to return based on the exception type.
- Custom App Exception - a custom exception class used to differentiate between handled exceptions that return a 400 response and unhandled exceptions that return a 500 response.
- Startup.cs - the ASP.NET Core startup class that adds the global error handler middleware to the application request pipeline.
- Example Error Service - an example service that shows how to throw a custom exception that will be handled by the global error handler.
The below example code snippets are from a boilerplate authentication api tutorial I posted recently, for the full tutorial or to download and test the code see ASP.NET Core 3.1 - Boilerplate API with Email Sign Up, Verification, Authentication & Forgot Password.
Global Error Handler Middleware
The global error handler middleware is used catch all exceptions thrown by the api in a single place, removing the need for duplicated error handling code throughout the application. It's configured as middleware in the Configure
method of the Startup.cs class.
Errors of type AppException
are treated as custom (app specific) errors that return a 400 Bad Request
response, the ASP.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 a custom app exception or not found exception that will be handled by the global error handler.
using Microsoft.AspNetCore.Http;
using System;
using System.Collections.Generic;
using System.Net;
using System.Text.Json;
using System.Threading.Tasks;
using WebApi.Helpers;
namespace WebApi.Middleware
{
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);
}
}
}
}
Custom App Exception
The app exception is a custom exception class used to differentiate between handled and unhandled exceptions. Handled exceptions are ones generated by the application and used to display friendly error messages to the client, for example business logic or validation exceptions caused by incorrect input from the user. Unhandled exceptions are generated by the .NET framework and can be caused by bugs in the application code.
using System;
using System.Globalization;
namespace WebApi.Helpers
{
// custom exception class for throwing application specific exceptions
// 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))
{
}
}
}
Startup Class
The startup class configures the services available to the ASP.NET Core Dependency Injection (DI) container in the ConfigureServices
method, and configures the ASP.NET Core request pipeline for the application in the Configure
method.
The global error handler middleware is configured on line 60
by calling app.UseMiddleware<ErrorHandlerMiddleware>();
using AutoMapper;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using System;
using WebApi.Helpers;
using WebApi.Middleware;
using WebApi.Services;
namespace WebApi
{
public class Startup
{
public IConfiguration Configuration { get; }
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
// add services to the DI container
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<DataContext>();
services.AddCors();
services.AddControllers().AddJsonOptions(x => x.JsonSerializerOptions.IgnoreNullValues = true);
services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies());
services.AddSwaggerGen();
// configure strongly typed settings object
services.Configure<AppSettings>(Configuration.GetSection("AppSettings"));
// configure DI for application services
services.AddScoped<IAccountService, AccountService>();
services.AddScoped<IEmailService, EmailService>();
}
// configure the HTTP request pipeline
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, DataContext context)
{
// migrate database changes on startup (includes initial db creation)
context.Database.Migrate();
// generated swagger json and swagger ui middleware
app.UseSwagger();
app.UseSwaggerUI(x => x.SwaggerEndpoint("/swagger/v1/swagger.json", "ASP.NET Core Sign-up and Verification API"));
app.UseRouting();
// global cors policy
app.UseCors(x => x
.SetIsOriginAllowed(origin => true)
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials());
// global error handler
app.UseMiddleware<ErrorHandlerMiddleware>();
// custom jwt auth middleware
app.UseMiddleware<JwtMiddleware>();
app.UseEndpoints(x => x.MapControllers());
}
}
}
Example Error 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.
using System.Collections.Generic;
using WebApi.Helpers;
namespace WebApi.Services
{
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 ASP.NET Core Help?
Search fiverr for freelance ASP.NET Core 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!