using AutobusApi.Application.Common.Exceptions; using Microsoft.AspNetCore.Mvc; namespace AutobusApi.Api.Middlewares; public class GlobalExceptionHandlerMiddleware : IMiddleware { private readonly Dictionary> _exceptionHandlers; public GlobalExceptionHandlerMiddleware() { // Register known exception types and handlers. _exceptionHandlers = new() { { typeof(ValidationException), HandleValidationException }, { typeof(RegistrationException), HandleRegistrationException }, { typeof(LoginException), HandleLoginException }, { typeof(RenewAccessTokenException), HandleRenewAccessTokenException }, { typeof(RevokeRefreshTokenException), HandleRevokeRefreshTokenException }, { typeof(NotFoundException), HandleNotFoundException }, }; } public async Task InvokeAsync(HttpContext context, RequestDelegate next) { try { await next(context); } catch (Exception exception) { var exceptionType = exception.GetType(); if (_exceptionHandlers.ContainsKey(exceptionType)) { await _exceptionHandlers[exceptionType].Invoke(context, exception); return; } await HandleUnhandledExceptionException(context, exception); } } private async Task HandleValidationException(HttpContext context, Exception exception) { var ex = (ValidationException) exception; context.Response.StatusCode = StatusCodes.Status400BadRequest; context.Response.ContentType = "application/problem+json"; await context.Response.WriteAsJsonAsync(new HttpValidationProblemDetails(ex.Errors) { Status = StatusCodes.Status400BadRequest, Type = "https://datatracker.ietf.org/doc/html/rfc7231#section-6.5.1", Detail = "Check provided information." }); } private async Task HandleRegistrationException(HttpContext context, Exception exception) { context.Response.StatusCode = StatusCodes.Status400BadRequest; context.Response.ContentType = "application/problem+json"; await context.Response.WriteAsJsonAsync(new ProblemDetails() { Status = StatusCodes.Status400BadRequest, Type = "https://datatracker.ietf.org/doc/html/rfc7231#section-6.5.1", Title = "Registration failed.", Detail = "Check your credentials." }); } private async Task HandleLoginException(HttpContext context, Exception exception) { context.Response.StatusCode = StatusCodes.Status400BadRequest; context.Response.ContentType = "application/problem+json"; await context.Response.WriteAsJsonAsync(new ProblemDetails() { Status = StatusCodes.Status400BadRequest, Type = "https://datatracker.ietf.org/doc/html/rfc7231#section-6.5.1", Title = "Login failed.", Detail = "Provided email and/or password are invalid." }); } private async Task HandleRenewAccessTokenException(HttpContext context, Exception exception) { context.Response.StatusCode = StatusCodes.Status400BadRequest; context.Response.ContentType = "application/problem+json"; await context.Response.WriteAsJsonAsync(new ProblemDetails() { Status = StatusCodes.Status400BadRequest, Type = "https://datatracker.ietf.org/doc/html/rfc7231#section-6.5.1", Title = "Access token renewal failed.", Detail = "Check validity of your refresh token." }); } private async Task HandleRevokeRefreshTokenException(HttpContext context, Exception exception) { context.Response.StatusCode = StatusCodes.Status400BadRequest; context.Response.ContentType = "application/problem+json"; await context.Response.WriteAsJsonAsync(new ProblemDetails() { Status = StatusCodes.Status400BadRequest, Type = "https://datatracker.ietf.org/doc/html/rfc7231#section-6.5.1", Title = "Refresh token revocation failed.", Detail = "Check validity of your refresh token." }); } private async Task HandleNotFoundException(HttpContext context, Exception exception) { context.Response.StatusCode = StatusCodes.Status404NotFound; context.Response.ContentType = "application/problem+json"; await context.Response.WriteAsJsonAsync(new ProblemDetails() { Status = StatusCodes.Status404NotFound, Type = "https://datatracker.ietf.org/doc/html/rfc7231#section-6.5.4", // Title = "Refresh token revocation failed.", // Detail = "Check validity of your refresh token." }); } private async Task HandleUnhandledExceptionException(HttpContext context, Exception exception) { context.Response.StatusCode = StatusCodes.Status500InternalServerError; context.Response.ContentType = "application/problem+json"; await context.Response.WriteAsJsonAsync(new ProblemDetails() { Status = StatusCodes.Status500InternalServerError, Type = "https://datatracker.ietf.org/doc/html/rfc7231#section-6.6.1", Title = "One or more internal server errors occured.", Detail = "Report this error to service's support team.", }); await Console.Error.WriteLineAsync(exception.Message); await Console.Error.WriteLineAsync(exception.StackTrace); } }