Files
the-biergarten-app/web/backend/API/API.Core/GlobalException.cs
Aaron Po c825d99b0a Refactor comments and update documentation for backend and pipeline (#246)
* Remove restating comments in backend

* Remove restating comments in frontend stories

* Remove restating comments in pipeline

* Remove restating comments in pipeline scripts

* Restore location-clear comment in BreweryRepository

* Remove stale/restating comments found in second pass

* Restore mock curated-data branch comment in main.cc

* Remove more restating/filler comments in backend

* Document account-management endpoints, compose files, and env generator

* Squashed commit of the following:

commit a8a5b8abd9969859c695092fae6990d03b2135ed
Author: Aaron Po <aaron.po97@gmail.com>
Date:   Sun Aug 23 23:53:56 2026 -0400

    Add reverse geocoding for brewery street addresses (#245)

    * Add geocoding to seed application, remove postal codes from generator

    Update comments

* Update pipeline class diagram for OpenAI generator

* Update pipeline activity diagram for brewery coordinates and OpenAI backend

* Add mailpit, production, db-only, and minimal environments to deployment diagram

* Fix authentication flow diagram for Identity migration

* Add account-management flows to authentication diagram

* Fix database schema diagram note referencing removed AuthRepository
2026-08-24 01:09:48 -04:00

104 lines
3.8 KiB
C#

using Domain.Exceptions;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Data.SqlClient;
using Shared.Contracts;
using ValidationException = FluentValidation.ValidationException;
namespace API.Core;
/// <summary>
/// MVC exception filter that converts unhandled exceptions raised by controller actions into
/// consistent JSON error responses with appropriate HTTP status codes.
/// </summary>
public class GlobalExceptionFilter(ILogger<GlobalExceptionFilter> logger) : IExceptionFilter
{
/// <summary>
/// Logs the exception and sets <see cref="ExceptionContext.Result" /> to an appropriate error response.
/// </summary>
/// <remarks>
/// Maps <see cref="FluentValidation.ValidationException" /> to 400, <see cref="ConflictException" /> to 409,
/// <see cref="NotFoundException" /> to 404, <see cref="UnauthorizedException" /> to 401,
/// <see cref="ForbiddenException" /> to 403, <see cref="SqlException" /> to 503,
/// <see cref="Domain.Exceptions.ValidationException" /> to 400, and anything else to 500.
/// </remarks>
public void OnException(ExceptionContext context)
{
logger.LogError(context.Exception, "Unhandled exception occurred");
switch (context.Exception)
{
case ValidationException fluentValidationException:
Dictionary<string, string[]> errors = fluentValidationException
.Errors.GroupBy(e => e.PropertyName)
.ToDictionary(g => g.Key, g => g.Select(e => e.ErrorMessage).ToArray());
context.Result = new BadRequestObjectResult(
new { message = "Validation failed", errors }
);
context.ExceptionHandled = true;
break;
case ConflictException ex:
context.Result = new ObjectResult(new ResponseBody { Message = ex.Message })
{
StatusCode = 409,
};
context.ExceptionHandled = true;
break;
case NotFoundException ex:
context.Result = new ObjectResult(new ResponseBody { Message = ex.Message })
{
StatusCode = 404,
};
context.ExceptionHandled = true;
break;
case UnauthorizedException ex:
context.Result = new ObjectResult(new ResponseBody { Message = ex.Message })
{
StatusCode = 401,
};
context.ExceptionHandled = true;
break;
case ForbiddenException ex:
context.Result = new ObjectResult(new ResponseBody { Message = ex.Message })
{
StatusCode = 403,
};
context.ExceptionHandled = true;
break;
case SqlException ex:
context.Result = new ObjectResult(
new ResponseBody { Message = "A database error occurred." }
)
{
StatusCode = 503,
};
context.ExceptionHandled = true;
break;
case Domain.Exceptions.ValidationException ex:
context.Result = new ObjectResult(new ResponseBody { Message = ex.Message })
{
StatusCode = 400,
};
context.ExceptionHandled = true;
break;
default:
context.Result = new ObjectResult(
new ResponseBody { Message = "An unexpected error occurred" }
)
{
StatusCode = 500,
};
context.ExceptionHandled = true;
break;
}
}
}