[backend/asp] Improve validation error handling

This commit is contained in:
Laura Hausmann 2024-07-10 02:42:42 +02:00
parent 09cda1a89e
commit 769a94d83c
No known key found for this signature in database
GPG key ID: D044E84C5BE01605
3 changed files with 24 additions and 25 deletions

View file

@ -9,7 +9,6 @@ using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.Extensions.ObjectPool; using Microsoft.Extensions.ObjectPool;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Newtonsoft.Json; using Newtonsoft.Json;
using JsonSerializer = System.Text.Json.JsonSerializer;
namespace Iceshrimp.Backend.Core.Extensions; namespace Iceshrimp.Backend.Core.Extensions;
@ -82,9 +81,8 @@ public static class MvcBuilderExtensions
var message = details.Title ?? "One or more validation errors occurred."; var message = details.Title ?? "One or more validation errors occurred.";
if (details.Detail != null) if (details.Detail != null)
message += $" - {details.Detail}"; 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);
}; };
}); });

View file

@ -76,37 +76,25 @@ public class ErrorHandlerMiddleware(
ctx.Response.StatusCode = (int)ce.StatusCode; ctx.Response.StatusCode = (int)ce.StatusCode;
ctx.Response.Headers.RequestId = ctx.TraceIdentifier; ctx.Response.Headers.RequestId = ctx.TraceIdentifier;
// @formatter:off
if (isMastodon) if (isMastodon)
await ctx.Response.WriteAsJsonAsync(new MastodonErrorResponse await ctx.Response.WriteAsJsonAsync(new MastodonErrorResponse
{ {
Error = verbosity >= ExceptionVerbosity.Basic Error = verbosity >= ExceptionVerbosity.Basic ? ce.Message : ce.StatusCode.ToString(),
? ce.Message Description = verbosity >= ExceptionVerbosity.Basic ? ce.Details : null
: ce.StatusCode.ToString(),
Description = verbosity >= ExceptionVerbosity.Basic
? ce.Details
: null
}); });
else else
await ctx.Response.WriteAsJsonAsync(new ErrorResponse await ctx.Response.WriteAsJsonAsync(new ErrorResponse
{ {
StatusCode = ctx.Response.StatusCode, StatusCode = ctx.Response.StatusCode,
Error = Error = verbosity >= ExceptionVerbosity.Basic ? ce.Error : ce.StatusCode.ToString(),
verbosity >= ExceptionVerbosity.Basic Message = verbosity >= ExceptionVerbosity.Basic ? ce.Message : null,
? ce.Error Details = verbosity == ExceptionVerbosity.Full ? ce.Details : null,
: ce.StatusCode.ToString(), Errors = verbosity == ExceptionVerbosity.Full ? (ce as ValidationException)?.Errors : null,
Message = Source = verbosity == ExceptionVerbosity.Full ? type : null,
verbosity >= ExceptionVerbosity.Basic RequestId = ctx.TraceIdentifier
? ce.Message
: null,
Details =
verbosity == ExceptionVerbosity.Full
? ce.Details
: null,
Source = verbosity == ExceptionVerbosity.Full
? type
: null,
RequestId = ctx.TraceIdentifier
}); });
// @formatter:on
var level = ce.SuppressLog ? LogLevel.Trace : LogLevel.Debug; 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 string Uri => uri;
} }
public class ValidationException(
HttpStatusCode statusCode,
string error,
string message,
IDictionary<string, string[]> errors
) : GracefulException(statusCode, error, message)
{
public IDictionary<string, string[]> Errors => errors;
}
public enum ExceptionVerbosity public enum ExceptionVerbosity
{ {
[SuppressMessage("ReSharper", "UnusedMember.Global")] [SuppressMessage("ReSharper", "UnusedMember.Global")]

View file

@ -14,6 +14,9 @@ public class ErrorResponse
[JI(Condition = JsonIgnoreCondition.WhenWritingNull)] [JI(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string? Details { get; set; } public string? Details { get; set; }
[JI(Condition = JsonIgnoreCondition.WhenWritingNull)]
public IDictionary<string, string[]>? Errors { get; set; }
[JI(Condition = JsonIgnoreCondition.WhenWritingNull)] [JI(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string? Source { get; set; } public string? Source { get; set; }