diff --git a/Iceshrimp.Backend/Core/Extensions/MvcBuilderExtensions.cs b/Iceshrimp.Backend/Core/Extensions/MvcBuilderExtensions.cs index 93e1fd5d..e75810a1 100644 --- a/Iceshrimp.Backend/Core/Extensions/MvcBuilderExtensions.cs +++ b/Iceshrimp.Backend/Core/Extensions/MvcBuilderExtensions.cs @@ -9,7 +9,6 @@ using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.Extensions.ObjectPool; using Microsoft.Extensions.Options; using Newtonsoft.Json; -using JsonSerializer = System.Text.Json.JsonSerializer; namespace Iceshrimp.Backend.Core.Extensions; @@ -82,9 +81,8 @@ public static class MvcBuilderExtensions var message = details.Title ?? "One or more validation errors occurred."; if (details.Detail != null) message += $" - {details.Detail}"; - var errors = JsonSerializer.Serialize(details.Errors); - throw new GracefulException(status, status.ToString(), message, errors); + throw new ValidationException(status, status.ToString(), message, details.Errors); }; }); diff --git a/Iceshrimp.Backend/Core/Middleware/ErrorHandlerMiddleware.cs b/Iceshrimp.Backend/Core/Middleware/ErrorHandlerMiddleware.cs index 19036900..74f7bb1f 100644 --- a/Iceshrimp.Backend/Core/Middleware/ErrorHandlerMiddleware.cs +++ b/Iceshrimp.Backend/Core/Middleware/ErrorHandlerMiddleware.cs @@ -76,37 +76,25 @@ public class ErrorHandlerMiddleware( ctx.Response.StatusCode = (int)ce.StatusCode; ctx.Response.Headers.RequestId = ctx.TraceIdentifier; + // @formatter:off if (isMastodon) await ctx.Response.WriteAsJsonAsync(new MastodonErrorResponse { - Error = verbosity >= ExceptionVerbosity.Basic - ? ce.Message - : ce.StatusCode.ToString(), - Description = verbosity >= ExceptionVerbosity.Basic - ? ce.Details - : null + Error = verbosity >= ExceptionVerbosity.Basic ? ce.Message : ce.StatusCode.ToString(), + Description = verbosity >= ExceptionVerbosity.Basic ? ce.Details : null }); else await ctx.Response.WriteAsJsonAsync(new ErrorResponse { StatusCode = ctx.Response.StatusCode, - Error = - verbosity >= ExceptionVerbosity.Basic - ? ce.Error - : ce.StatusCode.ToString(), - Message = - verbosity >= ExceptionVerbosity.Basic - ? ce.Message - : null, - Details = - verbosity == ExceptionVerbosity.Full - ? ce.Details - : null, - Source = verbosity == ExceptionVerbosity.Full - ? type - : null, - RequestId = ctx.TraceIdentifier + Error = verbosity >= ExceptionVerbosity.Basic ? ce.Error : ce.StatusCode.ToString(), + Message = verbosity >= ExceptionVerbosity.Basic ? ce.Message : null, + Details = verbosity == ExceptionVerbosity.Full ? ce.Details : null, + Errors = verbosity == ExceptionVerbosity.Full ? (ce as ValidationException)?.Errors : null, + Source = verbosity == ExceptionVerbosity.Full ? type : null, + RequestId = ctx.TraceIdentifier }); + // @formatter:on var level = ce.SuppressLog ? LogLevel.Trace : LogLevel.Debug; @@ -225,6 +213,16 @@ public class InstanceBlockedException(string uri, string? host = null) public string Uri => uri; } +public class ValidationException( + HttpStatusCode statusCode, + string error, + string message, + IDictionary errors +) : GracefulException(statusCode, error, message) +{ + public IDictionary Errors => errors; +} + public enum ExceptionVerbosity { [SuppressMessage("ReSharper", "UnusedMember.Global")] diff --git a/Iceshrimp.Shared/Schemas/Web/ErrorResponse.cs b/Iceshrimp.Shared/Schemas/Web/ErrorResponse.cs index 2e8dc49a..ca808204 100644 --- a/Iceshrimp.Shared/Schemas/Web/ErrorResponse.cs +++ b/Iceshrimp.Shared/Schemas/Web/ErrorResponse.cs @@ -14,6 +14,9 @@ public class ErrorResponse [JI(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? Details { get; set; } + [JI(Condition = JsonIgnoreCondition.WhenWritingNull)] + public IDictionary? Errors { get; set; } + [JI(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? Source { get; set; }