diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/ExceptionHandlerBase.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/ClientExceptionHandlerBase.cs similarity index 52% rename from src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/ExceptionHandlerBase.cs rename to src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/ClientExceptionHandlerBase.cs index 6c137be287..abfc8fea67 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/ExceptionHandlerBase.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/ClientExceptionHandlerBase.cs @@ -6,13 +6,12 @@ namespace Boilerplate.Client.Core.Services; -public abstract partial class ExceptionHandlerBase : IExceptionHandler +public abstract partial class ClientExceptionHandlerBase : SharedExceptionHandler, IExceptionHandler { [AutoInject] protected Bit.Butil.Console Console = default!; [AutoInject] protected ITelemetryContext TelemetryContext = default!; - [AutoInject] protected ILogger Logger = default!; + [AutoInject] protected ILogger Logger = default!; [AutoInject] protected readonly MessageBoxService MessageBoxService = default!; - [AutoInject] protected readonly IStringLocalizer Localizer = default!; public void Handle(Exception exception, Dictionary? parameters = null, @@ -35,14 +34,7 @@ protected virtual void Handle(Exception exception, Dictionary pa using (var scope = Logger.BeginScope(parameters.ToDictionary(i => i.Key, i => i.Value ?? string.Empty))) { - var exceptionMessageToLog = exception.Message; - var innerException = exception.InnerException; - - while (innerException is not null) - { - exceptionMessageToLog += $"{Environment.NewLine}{innerException.Message}"; - innerException = innerException.InnerException; - } + var exceptionMessageToLog = GetExceptionMessageToLog(exception); if (exception is KnownException) { @@ -54,8 +46,7 @@ protected virtual void Handle(Exception exception, Dictionary pa } } - string exceptionMessageToShow = (exception as KnownException)?.Message ?? - (isDevEnv ? exception.ToString() : Localizer[nameof(AppStrings.UnknownException)]); + string exceptionMessageToShow = GetExceptionMessageToShow(exception); MessageBoxService.Show(exceptionMessageToShow, Localizer[nameof(AppStrings.Error)]); @@ -64,29 +55,4 @@ protected virtual void Handle(Exception exception, Dictionary pa Debugger.Break(); } } - - protected Exception UnWrapException(Exception exception) - { - if (exception is AggregateException aggregateException) - { - return aggregateException.Flatten().InnerException ?? aggregateException; - } - else if (exception is TargetInvocationException) - { - return exception.InnerException ?? exception; - } - - return exception; - } - - protected bool IgnoreException(Exception exception) - { - if (exception is KnownException) - return false; - - return exception is TaskCanceledException || - exception is OperationCanceledException || - exception is TimeoutException || - (exception.InnerException is not null && IgnoreException(exception.InnerException)); - } } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Services/MauiExceptionHandler.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Services/MauiExceptionHandler.cs index aba5b8504f..d5ffa6e90f 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Services/MauiExceptionHandler.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Services/MauiExceptionHandler.cs @@ -7,7 +7,7 @@ namespace Boilerplate.Client.Maui.Services; /// Employing Microsoft.Extensions.Logging implementations (like Sentry.Extensions.Logging) will result in /// automatic exception logging due to the logger.LogError method call within the ExceptionHandlerBase class. /// -public partial class MauiExceptionHandler : ExceptionHandlerBase +public partial class MauiExceptionHandler : ClientExceptionHandlerBase { protected override void Handle(Exception exception, Dictionary parameters) { diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/Services/WebExceptionHandler.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/Services/WebExceptionHandler.cs index 2b6f05695e..8e7dd2c635 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/Services/WebExceptionHandler.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/Services/WebExceptionHandler.cs @@ -1,6 +1,6 @@ namespace Boilerplate.Client.Web.Services; -public partial class WebExceptionHandler : ExceptionHandlerBase +public partial class WebExceptionHandler : ClientExceptionHandlerBase { protected override void Handle(Exception exception, Dictionary parameters) { diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/Services/WindowsExceptionHandler.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/Services/WindowsExceptionHandler.cs index 03c98a0444..29f3aef7f1 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/Services/WindowsExceptionHandler.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/Services/WindowsExceptionHandler.cs @@ -6,7 +6,7 @@ /// Employing Microsoft.Extensions.Logging implementations (like Sentry.Extensions.Logging) will result in /// automatic exception logging due to the logger.LogError method call within the ExceptionHandlerBase class. /// -public partial class WindowsExceptionHandler : ExceptionHandlerBase +public partial class WindowsExceptionHandler : ClientExceptionHandlerBase { protected override void Handle(Exception exception, Dictionary parameters) { diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Services/ServerExceptionHandler.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Services/ServerExceptionHandler.cs index 4cbea7ccfd..f0d31bcd1c 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Services/ServerExceptionHandler.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Services/ServerExceptionHandler.cs @@ -1,15 +1,13 @@ using System.Net; -using System.Reflection; using System.Diagnostics; using Microsoft.Net.Http.Headers; using Microsoft.AspNetCore.Diagnostics; namespace Boilerplate.Server.Api.Services; -public partial class ServerExceptionHandler : IExceptionHandler +public partial class ServerExceptionHandler : SharedExceptionHandler, IExceptionHandler { [AutoInject] private IWebHostEnvironment webHostEnvironment = default!; - [AutoInject] private IStringLocalizer localizer = default!; [AutoInject] private JsonSerializerOptions jsonSerializerOptions = default!; public async ValueTask TryHandleAsync(HttpContext httpContext, Exception e, CancellationToken cancellationToken) @@ -22,13 +20,13 @@ public async ValueTask TryHandleAsync(HttpContext httpContext, Exception e // The details of all of the exceptions are returned only in dev mode. in any other modes like production, only the details of the known exceptions are returned. var key = knownException?.Key ?? nameof(UnknownException); - var message = knownException?.Message ?? (webHostEnvironment.IsDevelopment() ? exception.Message : localizer[nameof(UnknownException)]); + var message = GetExceptionMessageToShow(exception); var statusCode = (int)(exception is RestException restExp ? restExp.StatusCode : HttpStatusCode.InternalServerError); if (exception is KnownException && message == key) { - message = localizer[message]; + message = Localizer[message]; } var restExceptionPayload = new RestErrorInfo @@ -49,18 +47,4 @@ public async ValueTask TryHandleAsync(HttpContext httpContext, Exception e return true; } - - private Exception UnWrapException(Exception exception) - { - if (exception is AggregateException aggregateException) - { - return aggregateException.Flatten().InnerException ?? aggregateException; - } - else if (exception is TargetInvocationException) - { - return exception.InnerException ?? exception; - } - - return exception; - } } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Services/SharedExceptionHandler.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Services/SharedExceptionHandler.cs new file mode 100644 index 0000000000..6a54ab1abb --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Services/SharedExceptionHandler.cs @@ -0,0 +1,58 @@ +using System.Reflection; + +namespace Boilerplate.Shared.Services; + +public partial class SharedExceptionHandler +{ + [AutoInject] protected IStringLocalizer Localizer { get; set; } = default!; + + protected string GetExceptionMessageToShow(Exception exception) + { + if (exception is KnownException) + return exception.Message; + + if (AppEnvironment.IsDev()) + return exception.ToString(); + + return Localizer[nameof(AppStrings.UnknownException)]; + } + + protected string GetExceptionMessageToLog(Exception exception) + { + var exceptionMessageToLog = exception.Message; + var innerException = exception.InnerException; + + while (innerException is not null) + { + exceptionMessageToLog += $"{Environment.NewLine}{innerException.Message}"; + innerException = innerException.InnerException; + } + + return exceptionMessageToLog; + } + + protected Exception UnWrapException(Exception exception) + { + if (exception is AggregateException aggregateException) + { + return aggregateException.Flatten().InnerException ?? aggregateException; + } + else if (exception is TargetInvocationException) + { + return exception.InnerException ?? exception; + } + + return exception; + } + + protected bool IgnoreException(Exception exception) + { + if (exception is KnownException) + return false; + + return exception is TaskCanceledException || + exception is OperationCanceledException || + exception is TimeoutException || + (exception.InnerException is not null && IgnoreException(exception.InnerException)); + } +}