using System.Net; using Foxchat.Core.Models.Http; using Microsoft.AspNetCore.Http; using Newtonsoft.Json; using HttpApiError = Foxchat.Core.Models.Http.ApiError; namespace Foxchat.Core.Middleware; public class ErrorHandlerMiddleware(ILogger baseLogger) : IMiddleware { public async Task InvokeAsync(HttpContext ctx, RequestDelegate next) { try { await next(ctx); } catch (Exception e) { var type = e.TargetSite?.DeclaringType ?? typeof(ErrorHandlerMiddleware); var typeName = e.TargetSite?.DeclaringType?.FullName ?? ""; var logger = baseLogger.ForContext(type); if (ctx.Response.HasStarted) { logger.Error(e, "Error in {ClassName} ({Path}) after response started being sent", typeName, ctx.Request.Path); } if (e is ApiError ae) { ctx.Response.StatusCode = (int)ae.StatusCode; ctx.Response.Headers.RequestId = ctx.TraceIdentifier; ctx.Response.ContentType = "application/json; charset=utf-8"; if (ae is ApiError.OutgoingFederationError ofe) { await ctx.Response.WriteAsync(JsonConvert.SerializeObject(new HttpApiError { Status = (int)ofe.StatusCode, Code = ErrorCode.OutgoingFederationError, Message = ofe.Message, OriginalError = ofe.InnerError })); return; } else if (ae is ApiError.Forbidden fe) { await ctx.Response.WriteAsync(JsonConvert.SerializeObject(new HttpApiError { Status = (int)fe.StatusCode, Code = ErrorCode.Forbidden, Message = fe.Message, Scopes = fe.Scopes.Length > 0 ? fe.Scopes : null })); return; } await ctx.Response.WriteAsync(JsonConvert.SerializeObject(new HttpApiError { Status = (int)ae.StatusCode, Code = ErrorCode.GenericApiError, Message = ae.Message, })); return; } if (e is FoxchatError fce) { logger.Error(fce.Inner ?? fce, "Exception in {ClassName} ({Path})", typeName, ctx.Request.Path); } else { logger.Error(e, "Exception in {ClassName} ({Path})", typeName, ctx.Request.Path); } ctx.Response.StatusCode = (int)HttpStatusCode.InternalServerError; ctx.Response.Headers.RequestId = ctx.TraceIdentifier; ctx.Response.ContentType = "application/json; charset=utf-8"; await ctx.Response.WriteAsync(JsonConvert.SerializeObject(new HttpApiError { Status = (int)HttpStatusCode.InternalServerError, Code = ErrorCode.InternalServerError, Message = "Internal server error", })); } } }