using System.ComponentModel.DataAnnotations; using e_suite.API.Common; using e_suite.API.Common.exceptions; using e_suite.Database.Core.Extensions.Exceptions; using Microsoft.AspNetCore.Mvc; using Newtonsoft.Json; namespace eSuite.API.Middleware; /// /// Exception capture middleware /// public class ExceptionCapture { private readonly RequestDelegate _next; private readonly IExceptionLogManager _exceptionLogManager; /// /// Default constructor /// /// /// public ExceptionCapture(RequestDelegate next, IExceptionLogManager exceptionLogManager) { _next = next; _exceptionLogManager = exceptionLogManager; } /// /// Called to wrap the controller method with general graceful error handling /// /// /// public async Task InvokeAsync(HttpContext context) { try { await _next.Invoke(context); } catch (GuidMismatchException ex) { await BadRequestResponse(context, ex); } catch (NotFoundException ex) { await NotFoundResponse(context, ex); } catch (ExistsException ex) { await BadRequestResponse(context, ex); } catch (InvalidOperationException ex) { await BadRequestResponse(context, ex); } catch( ValidationException ex) { await BadRequestResponse(context, ex); } catch (ArgumentException ex) { await BadRequestResponse(context, ex); } catch (InvalidReferenceObjectId ex) { await BadRequestResponse(context, ex); } catch (TokenInvalidException ex) { await BadRequestResponse(context, ex); } catch (InvalidEmailException ex) { await BadRequestResponse(context, ex); } catch (MinimumRangeException ex) { await BadRequestResponse(context, ex); } catch (MaximumRangeException ex) { await BadRequestResponse(context, ex); } catch (OperationCanceledException ex) { await BadRequestResponse(context, ex, "Request cancelled"); } catch (Exception ex) { var supportingData = new { User = context.User.Identity != null ? context.User.Identity.Name : string.Empty, context.Request.Query, context.Request.Method, context.Request.Path, context.Request.Headers, context.Request.Cookies, context.Request.QueryString, context.Items }.ToJson(); var exceptionId = await _exceptionLogManager.LogException(ex, "e-suite API", supportingData, CancellationToken.None); await InternalServerErrorResponse(context, ex, exceptionId); } } private static async Task BadRequestResponse(HttpContext context, Exception ex, string title = "Bad request") { var problemDetails = new ProblemDetails { Title = title, Detail = ex.Message }; context.Response.StatusCode = StatusCodes.Status400BadRequest; context.Response.ContentType = "application/json"; await context.Response.WriteAsync(problemDetails.ToJson()); } private static async Task NotFoundResponse(HttpContext context, Exception ex, string title = "Not found") { var problemDetails = new ProblemDetails { Title = title, Detail = ex.Message }; context.Response.StatusCode = StatusCodes.Status404NotFound; context.Response.ContentType = "application/json"; await context.Response.WriteAsync(problemDetails.ToJson()); } private static async Task InternalServerErrorResponse(HttpContext context, Exception ex, long exceptionId, string title = "Internal Server Error") { var problemDetails = new ProblemDetails { Title = title, Detail = ex.Message, Instance = exceptionId.ToString() }; context.Response.StatusCode = StatusCodes.Status500InternalServerError; context.Response.ContentType = "application/json"; await context.Response.WriteAsync(problemDetails.ToJson()); } } /// /// /// public static class ObjectExtensions { /// /// Serialize the current object to Json /// /// /// public static string ToJson(this object value) { var options = new JsonSerializerSettings { Formatting = Formatting.Indented, ReferenceLoopHandling = ReferenceLoopHandling.Ignore, NullValueHandling = NullValueHandling.Include }; return JsonConvert.SerializeObject(value, options); } }