👉🏼 Click here to Join I ❤️ .NET WhatsApp Channel to get 🔔 notified about new articles and other updates.
Global Exception Handling in ASP.NET WEB API

Global Exception Handling in ASP.NET WEB API

Authors - Abdul Rahman (Content Writer), Regina Sharon (Graphic Designer)

Web API

22 Articles

Improve

Table of Contents

  1. What we gonna do?
  2. Why we gonna do?
  3. How we gonna do?
  4. Summary

What we gonna do?

In this article, let's learn about Global Exception Handling in Web API in ASP.NET Core.

Why we gonna do?

Global Exception Handling allows us to handle exceptions globally in one single place inside the application rather than scattering try-catch blocks everywhere in the code base. This is a good and clean practice that simplifies code maintenance and improves readability.

Errors are inevitable in APIs. To standardize error responses, ASP.NET Core supports the Problem Details payload as defined by RFC 7807. By enabling the default implementation of IProblemDetailsService through AddProblemDetails, we can automatically generate structured error responses. This approach integrates seamlessly with middlewares like the exception handler and developer exception page, providing clean and consistent error details in both production and development environments. This simplifies debugging and enhances the API's usability.

How we gonna do?

To implement Global Exception Handling, follow these steps:

  1. Starting .NET 8, We can achieve this using the AddProblemDetails to Get structured error response defined in RFC 7807 standard.

    
    var builder = WebApplication.CreateBuilder(args);
    
    builder.Services
    .AddProblemDetails(options =>
        options.CustomizeProblemDetails = (ctx) =>
        {
            var hostEnv = ctx.HttpContext.RequestServices.GetRequiredService<IHostEnvironment>();
            var logger = ctx.HttpContext.RequestServices.GetRequiredService<ILogger<ProblemDetails>>();
    
            // exception will hold the thrown exception
            var exception = ctx.HttpContext.Features.Get<IExceptionHandlerFeature>()?.Error;
            
            if (ctx.ProblemDetails.Status == 500)
            {
                logger.LogCritical(exception, exception!.Message);
                
                if (exception is DomainException domainException)
                {
                    ctx.ProblemDetails.Title = "An error occurred in our API. Use the trace id when contacting us.";
                    ctx.ProblemDetails.Detail = domainException.Message;
                }
                else if (exception is Exception ex)
                {
                    ctx.ProblemDetails.Title = "An unexpected fault happened. Try again later.";
                    ctx.ProblemDetails.Detail = hostEnv.IsProduction() ? "An unexpected fault happened. Try again later." : ex.Message;
                }
            }
        }
    )
                        
  2. Register the middleware as the first middleware in the pipeline:

    
    var builder = WebApplication.CreateBuilder(args);
    
    var app = builder.Build();
    
    app.UseExceptionHandler() // Use this to handle exceptions globally with problem details support. If this is enabled below line can be commented.
       .UseRouting()
       .UseEndpoints(endpoints =>
       {
           endpoints.MapControllers();
       });;
       
    app.Run();
                        
  3. The alternative is to Create a Global Exception Middleware as shown below if you want a custom response format implementation:

    
    using System.Net;
    
    namespace ILoveDotNet.Middlewares;
    
    public sealed class ExceptionHandlerMiddleware : IMiddleware
    {
        private readonly IWebHostEnvironment _env;
        private readonly ILogger<ExceptionHandlerMiddleware> _logger;
    
        public ExceptionHandlerMiddleware(IWebHostEnvironment env, ILogger<ExceptionHandlerMiddleware> logger)
        {
            _env = env;
            _logger = logger;
        }
    
        public async Task InvokeAsync(HttpContext context, RequestDelegate next)
        {
            try
            {
                await next(context);
            }
            catch (DomainException ex)
     		{
     			await HandleDomainExceptionAsync(context, ex);
     		}
            catch (Exception ex)
            {
                await HandleExceptionAsync(context, ex);
            }
        }
    
        private Task HandleDomainExceptionAsync(HttpContext context, DomainException exception)
    	{
    		logger.LogCritical(exception, exception.Message);
    		string result = exception.Message;
    		context.Response.ContentType = MediaTypeNames.Application.Json;
    		context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
    		return context.Response.WriteAsync(result);
    	}
    
        private Task HandleExceptionAsync(HttpContext context, Exception exception)
        {
            _logger.LogCritical(exception, exception.Message);
            var result = _env.IsProduction() ? "An unexpected fault happened. Try again later." : exception.Message;
            context.Response.ContentType = MediaTypeNames.Application.Json;
            context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
            return context.Response.WriteAsync(result);
        }
    }
    
    public static class ExceptionHandlerMiddlewareExtensions
    {
        public static IApplicationBuilder UseGlobalExceptionHandler(this IApplicationBuilder builder)
        {
            return builder.UseMiddleware<ExceptionHandlerMiddleware>();
        }
    }
                        

    This middleware inherits from IMiddleware and catches exceptions thrown from the application. It writes a general error message in production and the actual error message in non-production environments.

  4. Register the middleware as the first middleware in the pipeline:

    
    var builder = WebApplication.CreateBuilder(args);
    
    var app = builder.Build();
    
    app.UseGlobalExceptionHandler()
       .UseRouting()
       .UseEndpoints(endpoints =>
       {
           endpoints.MapControllers();
       });;
       
    app.Run();
                        

    Registering it first ensures that all exceptions are caught. If registered later, some exceptions might be missed.

Summary

In this article, we explored the simplest and easiest way to handle exceptions globally in ASP.NET Web API using a custom middleware. This approach centralizes exception handling, reduces code duplication, and improves maintainability.

👉🏼 Click here to Join I ❤️ .NET WhatsApp Channel to get 🔔 notified about new articles and other updates.
  • Web API
  • Global
  • Exception Handling
  • Error Handling