mirror of
https://github.com/aaronpo97/the-biergarten-app.git
synced 2026-10-15 01:54:43 +00:00
Fix CA1873 warning and consolidate test fixtures (#313)
This commit is contained in:
@@ -6,18 +6,11 @@ using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Shared.Application.Behaviors;
|
||||
|
||||
/// <summary>
|
||||
/// MediatR pipeline behavior that logs unhandled exceptions together with the request type and
|
||||
/// payload, then rethrows them unchanged for <c>GlobalExceptionFilter</c> to map to a status code.
|
||||
/// </summary>
|
||||
public class UnhandledExceptionLoggingBehavior<TRequest, TResponse>(
|
||||
ILogger<UnhandledExceptionLoggingBehavior<TRequest, TResponse>> logger
|
||||
) : IPipelineBehavior<TRequest, TResponse>
|
||||
where TRequest : IRequest<TResponse>
|
||||
public abstract class JsonLogger
|
||||
{
|
||||
private static readonly string[] SensitiveFieldNames = ["password", "token"];
|
||||
|
||||
private static readonly JsonSerializerOptions SerializerOptions = new()
|
||||
protected static readonly JsonSerializerOptions SerializerOptions = new()
|
||||
{
|
||||
TypeInfoResolver = new DefaultJsonTypeInfoResolver
|
||||
{
|
||||
@@ -25,46 +18,6 @@ public class UnhandledExceptionLoggingBehavior<TRequest, TResponse>(
|
||||
},
|
||||
};
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<TResponse> Handle(
|
||||
TRequest request,
|
||||
RequestHandlerDelegate<TResponse> next,
|
||||
CancellationToken cancellationToken
|
||||
)
|
||||
{
|
||||
try
|
||||
{
|
||||
return await next();
|
||||
}
|
||||
catch (ValidationException)
|
||||
{
|
||||
// Expected: ValidationBehavior already reports these as request errors, not incidents.
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogError(
|
||||
ex,
|
||||
"Unhandled exception while processing {RequestName}: {RequestPayload}",
|
||||
typeof(TRequest).Name,
|
||||
Serialize(request)
|
||||
);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private static string Serialize(TRequest request)
|
||||
{
|
||||
try
|
||||
{
|
||||
return JsonSerializer.Serialize(request, SerializerOptions);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return "<unable to serialize request payload>";
|
||||
}
|
||||
}
|
||||
|
||||
private static void RedactSensitiveProperties(JsonTypeInfo typeInfo)
|
||||
{
|
||||
if (typeInfo.Kind != JsonTypeInfoKind.Object)
|
||||
@@ -84,3 +37,71 @@ public class UnhandledExceptionLoggingBehavior<TRequest, TResponse>(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// MediatR pipeline behavior that logs unhandled exceptions together with the
|
||||
/// request type and
|
||||
/// payload, then rethrows them unchanged for <c>GlobalExceptionFilter</c> to
|
||||
/// map to a status code.
|
||||
/// </summary>
|
||||
public partial class UnhandledExceptionLoggingBehavior<TRequest, TResponse>
|
||||
: JsonLogger,
|
||||
IPipelineBehavior<TRequest, TResponse>
|
||||
where TRequest : IRequest<TResponse>
|
||||
{
|
||||
private readonly ILogger<UnhandledExceptionLoggingBehavior<TRequest, TResponse>> _logger;
|
||||
|
||||
public UnhandledExceptionLoggingBehavior(
|
||||
ILogger<UnhandledExceptionLoggingBehavior<TRequest, TResponse>> logger
|
||||
)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<TResponse> Handle(
|
||||
TRequest request,
|
||||
RequestHandlerDelegate<TResponse> next,
|
||||
CancellationToken cancellationToken
|
||||
)
|
||||
{
|
||||
try
|
||||
{
|
||||
return await next();
|
||||
}
|
||||
catch (ValidationException)
|
||||
{
|
||||
// Expected: ValidationBehavior already reports these as request errors
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (_logger.IsEnabled(LogLevel.Error))
|
||||
LogUnhandledException(ex, typeof(TRequest).Name, Serialize(request));
|
||||
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private static string Serialize(TRequest request)
|
||||
{
|
||||
try
|
||||
{
|
||||
return JsonSerializer.Serialize(request, SerializerOptions);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return "<unable to serialize request payload>";
|
||||
}
|
||||
}
|
||||
|
||||
[LoggerMessage(
|
||||
Level = LogLevel.Error,
|
||||
Message = "Unhandled exception while processing {RequestName}: {RequestPayload}"
|
||||
)]
|
||||
private partial void LogUnhandledException(
|
||||
Exception ex,
|
||||
string requestName,
|
||||
string requestPayload
|
||||
);
|
||||
}
|
||||
|
||||
@@ -5,14 +5,15 @@ using MediatR;
|
||||
namespace Shared.Application.Behaviors;
|
||||
|
||||
/// <summary>
|
||||
/// MediatR pipeline behavior that runs registered FluentValidation validators before the handler,
|
||||
/// MediatR pipeline behavior that runs registered FluentValidation validators
|
||||
/// before the handler,
|
||||
/// throwing <see cref="ValidationException" /> if any report failures.
|
||||
/// </summary>
|
||||
public class ValidationBehavior<TRequest, TResponse>(IEnumerable<IValidator<TRequest>> validators)
|
||||
: IPipelineBehavior<TRequest, TResponse>
|
||||
where TRequest : IRequest<TResponse>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
/// <inheritdoc />
|
||||
public async Task<TResponse> Handle(
|
||||
TRequest request,
|
||||
RequestHandlerDelegate<TResponse> next,
|
||||
|
||||
Reference in New Issue
Block a user