From fc2424b8515b796b9f2d534c821dd32e2bfb5952 Mon Sep 17 00:00:00 2001 From: Hamed Shirbandi Date: Wed, 4 Oct 2023 20:42:39 +0200 Subject: [PATCH 01/10] refactor: log managed exceptions --- .../Exceptions/ApplicationException.cs | 4 +-- ...sHandler.cs => ManagedExceptionHandler.cs} | 36 ++++++------------- .../Exceptions/ValidationException.cs | 4 +-- .../Services/ApplicationExtensions.cs | 2 +- .../Contracts/Exceptions/DomainException.cs | 14 ++++++++ .../Domain/Exceptions/DomainException.cs | 5 +-- 6 files changed, 33 insertions(+), 32 deletions(-) rename src/1-BuildingBlocks/Application/Exceptions/{ApplicationExceptionsHandler.cs => ManagedExceptionHandler.cs} (58%) create mode 100644 src/1-BuildingBlocks/Contracts/Exceptions/DomainException.cs diff --git a/src/1-BuildingBlocks/Application/Exceptions/ApplicationException.cs b/src/1-BuildingBlocks/Application/Exceptions/ApplicationException.cs index 22e0d0324..8df37d759 100644 --- a/src/1-BuildingBlocks/Application/Exceptions/ApplicationException.cs +++ b/src/1-BuildingBlocks/Application/Exceptions/ApplicationException.cs @@ -1,4 +1,4 @@ -using TaskoMask.BuildingBlocks.Domain.Exceptions; +using TaskoMask.BuildingBlocks.Contracts.Exceptions; namespace TaskoMask.BuildingBlocks.Application.Exceptions { @@ -6,7 +6,7 @@ namespace TaskoMask.BuildingBlocks.Application.Exceptions /// /// /// - public class ApplicationException : DomainException + public class ApplicationException : ManagedException { #region Ctors diff --git a/src/1-BuildingBlocks/Application/Exceptions/ApplicationExceptionsHandler.cs b/src/1-BuildingBlocks/Application/Exceptions/ManagedExceptionHandler.cs similarity index 58% rename from src/1-BuildingBlocks/Application/Exceptions/ApplicationExceptionsHandler.cs rename to src/1-BuildingBlocks/Application/Exceptions/ManagedExceptionHandler.cs index ef535686f..87710acd2 100644 --- a/src/1-BuildingBlocks/Application/Exceptions/ApplicationExceptionsHandler.cs +++ b/src/1-BuildingBlocks/Application/Exceptions/ManagedExceptionHandler.cs @@ -1,39 +1,39 @@ using TaskoMask.BuildingBlocks.Application.Notifications; -using MediatR; using MediatR.Pipeline; using System.Threading; using System.Threading.Tasks; -using TaskoMask.BuildingBlocks.Domain.Exceptions; +using Microsoft.Extensions.Logging; +using TaskoMask.BuildingBlocks.Contracts.Exceptions; +using System.Text.Json; namespace TaskoMask.BuildingBlocks.Application.Exceptions { /// - /// + /// Handle all managed exceptions (DomainException,ApplicationException,ValidationException) /// - public class ApplicationExceptionsHandler - : IRequestExceptionHandler where TException : DomainException + public class ManagedExceptionHandler + : IRequestExceptionHandler where TException : ManagedException { #region Fields private readonly INotificationHandler _notifications; - + private readonly ILogger _logger; #endregion - #region Ctors - public ApplicationExceptionsHandler(INotificationHandler notifications) + public ManagedExceptionHandler(INotificationHandler notifications, ILogger logger) { _notifications = notifications; + this._logger = logger; } #endregion - #region Handler @@ -45,25 +45,11 @@ public Task Handle(TRequest request, TException exception, RequestExceptionHandl { var exceptionType = exception.GetType(); - //notification exception error message if exist + //notify exception message if any if (!string.IsNullOrEmpty(exception.Message)) _notifications.Add(exceptionType.Name, exception.Message); - - if (exceptionType == typeof(ApplicationException)) - { - //log ApplicationException ... - } - - else if (exceptionType == typeof(ValidationException)) - { - //log ValidationException ... - } - - else if (exceptionType == typeof(DomainException)) - { - //log DomainException ... - } + _logger.LogWarning(exception, $"request : {JsonSerializer.Serialize(request)}"); state.SetHandled(default); diff --git a/src/1-BuildingBlocks/Application/Exceptions/ValidationException.cs b/src/1-BuildingBlocks/Application/Exceptions/ValidationException.cs index f4a3d8df7..4660eebfd 100644 --- a/src/1-BuildingBlocks/Application/Exceptions/ValidationException.cs +++ b/src/1-BuildingBlocks/Application/Exceptions/ValidationException.cs @@ -1,4 +1,4 @@ -using TaskoMask.BuildingBlocks.Domain.Exceptions; +using TaskoMask.BuildingBlocks.Contracts.Exceptions; namespace TaskoMask.BuildingBlocks.Application.Exceptions { @@ -6,7 +6,7 @@ namespace TaskoMask.BuildingBlocks.Application.Exceptions /// /// /// - public class ValidationException : DomainException + public class ValidationException : ManagedException { #region Ctors diff --git a/src/1-BuildingBlocks/Application/Services/ApplicationExtensions.cs b/src/1-BuildingBlocks/Application/Services/ApplicationExtensions.cs index d7bd06063..3de94d051 100644 --- a/src/1-BuildingBlocks/Application/Services/ApplicationExtensions.cs +++ b/src/1-BuildingBlocks/Application/Services/ApplicationExtensions.cs @@ -17,7 +17,7 @@ public static void AddBuildingBlocksApplication(this IServiceCollection services if (services == null) throw new ArgumentNullException(nameof(services)); - services.AddApplicationExceptionsHandler(); + services.AddApplicationExceptionHandlers(); services.AddApplicationBehaviors(validatorAssemblyMarkerType); services.AddDomainNotificationHandler(); } diff --git a/src/1-BuildingBlocks/Contracts/Exceptions/DomainException.cs b/src/1-BuildingBlocks/Contracts/Exceptions/DomainException.cs new file mode 100644 index 000000000..2688e90cf --- /dev/null +++ b/src/1-BuildingBlocks/Contracts/Exceptions/DomainException.cs @@ -0,0 +1,14 @@ +using System; +namespace TaskoMask.BuildingBlocks.Contracts.Exceptions +{ + /// + /// + /// + public class ManagedException : Exception + { + public ManagedException(string message): base(message) + { + } + + } +} \ No newline at end of file diff --git a/src/1-BuildingBlocks/Domain/Exceptions/DomainException.cs b/src/1-BuildingBlocks/Domain/Exceptions/DomainException.cs index 6053b2c4d..6f62180a4 100644 --- a/src/1-BuildingBlocks/Domain/Exceptions/DomainException.cs +++ b/src/1-BuildingBlocks/Domain/Exceptions/DomainException.cs @@ -1,10 +1,11 @@ -using System; +using TaskoMask.BuildingBlocks.Contracts.Exceptions; + namespace TaskoMask.BuildingBlocks.Domain.Exceptions { /// /// /// - public class DomainException : Exception + public class DomainException : ManagedException { public DomainException(string message): base(message) { From f9e01c2fbe8c9c20b0b049a31dee640edd4ae5a9 Mon Sep 17 00:00:00 2001 From: Hamed Shirbandi Date: Wed, 4 Oct 2023 20:43:05 +0200 Subject: [PATCH 02/10] chore: rename behavior extension class --- .../Behaviors/{BehaviorsExtensions.cs => BehaviorExtensions.cs} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename src/1-BuildingBlocks/Application/Behaviors/{BehaviorsExtensions.cs => BehaviorExtensions.cs} (97%) diff --git a/src/1-BuildingBlocks/Application/Behaviors/BehaviorsExtensions.cs b/src/1-BuildingBlocks/Application/Behaviors/BehaviorExtensions.cs similarity index 97% rename from src/1-BuildingBlocks/Application/Behaviors/BehaviorsExtensions.cs rename to src/1-BuildingBlocks/Application/Behaviors/BehaviorExtensions.cs index 0129b7c7e..398ec42ba 100644 --- a/src/1-BuildingBlocks/Application/Behaviors/BehaviorsExtensions.cs +++ b/src/1-BuildingBlocks/Application/Behaviors/BehaviorExtensions.cs @@ -6,7 +6,7 @@ namespace TaskoMask.BuildingBlocks.Application.Behaviors { - public static class BehaviorsExtensions + public static class BehaviorExtensions { From d2ae4b932fed1d8f6c8715a1954925d320d51aa3 Mon Sep 17 00:00:00 2001 From: Hamed Shirbandi Date: Wed, 4 Oct 2023 20:44:12 +0200 Subject: [PATCH 03/10] feat: handle and log unmanaged exceptions --- .../Exceptions/ExceptionExtensions.cs | 38 ++++++++++++ .../Exceptions/ExceptionsExtensions.cs | 17 ------ .../Exceptions/UnmanagedExceptionHandler.cs | 58 +++++++++++++++++++ 3 files changed, 96 insertions(+), 17 deletions(-) create mode 100644 src/1-BuildingBlocks/Application/Exceptions/ExceptionExtensions.cs delete mode 100644 src/1-BuildingBlocks/Application/Exceptions/ExceptionsExtensions.cs create mode 100644 src/1-BuildingBlocks/Application/Exceptions/UnmanagedExceptionHandler.cs diff --git a/src/1-BuildingBlocks/Application/Exceptions/ExceptionExtensions.cs b/src/1-BuildingBlocks/Application/Exceptions/ExceptionExtensions.cs new file mode 100644 index 000000000..f5f6c3840 --- /dev/null +++ b/src/1-BuildingBlocks/Application/Exceptions/ExceptionExtensions.cs @@ -0,0 +1,38 @@ +using MediatR.Pipeline; +using Microsoft.Extensions.DependencyInjection; + +namespace TaskoMask.BuildingBlocks.Application.Exceptions +{ + public static class ExceptionExtensions + { + + /// + /// + /// + public static void AddApplicationExceptionHandlers(this IServiceCollection services) + { + services.AddManagedExceptionsHandler(); + services.AddUnmanagedExceptionsHandler(); + } + + + + /// + /// + /// + private static void AddManagedExceptionsHandler(this IServiceCollection services) + { + services.AddScoped(typeof(IRequestExceptionHandler<,,>), typeof(ManagedExceptionHandler<,,>)); + } + + + /// + /// + /// + private static void AddUnmanagedExceptionsHandler(this IServiceCollection services) + { + services.AddScoped(typeof(IRequestExceptionHandler<,,>), typeof(UnmanagedExceptionHandler<,,>)); + } + + } +} diff --git a/src/1-BuildingBlocks/Application/Exceptions/ExceptionsExtensions.cs b/src/1-BuildingBlocks/Application/Exceptions/ExceptionsExtensions.cs deleted file mode 100644 index fddd7933a..000000000 --- a/src/1-BuildingBlocks/Application/Exceptions/ExceptionsExtensions.cs +++ /dev/null @@ -1,17 +0,0 @@ -using MediatR.Pipeline; -using Microsoft.Extensions.DependencyInjection; - -namespace TaskoMask.BuildingBlocks.Application.Exceptions -{ - public static class ExceptionsExtensions - { - - /// - /// - /// - public static void AddApplicationExceptionsHandler(this IServiceCollection services) - { - services.AddScoped(typeof(IRequestExceptionHandler<,,>), typeof(ApplicationExceptionsHandler<,,>)); - } - } -} diff --git a/src/1-BuildingBlocks/Application/Exceptions/UnmanagedExceptionHandler.cs b/src/1-BuildingBlocks/Application/Exceptions/UnmanagedExceptionHandler.cs new file mode 100644 index 000000000..726d4eb8b --- /dev/null +++ b/src/1-BuildingBlocks/Application/Exceptions/UnmanagedExceptionHandler.cs @@ -0,0 +1,58 @@ +using TaskoMask.BuildingBlocks.Application.Notifications; +using MediatR.Pipeline; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using System; +using System.Text.Json; + +namespace TaskoMask.BuildingBlocks.Application.Exceptions +{ + /// + /// Handle all unmanaged exceptions + /// + public class UnmanagedExceptionHandler + : IRequestExceptionHandler where TException : Exception + { + #region Fields + + + private readonly INotificationHandler _notifications; + private readonly ILogger _logger; + + #endregion + + #region Ctors + + + public UnmanagedExceptionHandler(INotificationHandler notifications, ILogger logger) + { + _notifications = notifications; + this._logger = logger; + } + + + #endregion + + #region Handler + + + + /// + /// + /// + public Task Handle(TRequest request, TException exception, RequestExceptionHandlerState state, CancellationToken cancellationToken) + { + _notifications.Add("Unknown Exception", "Unknown exception happened"); + + _logger.LogError(exception, $"request : {JsonSerializer.Serialize(request)}"); + + state.SetHandled(default); + + return Task.CompletedTask; + } + + + #endregion + } +} \ No newline at end of file From efd31a20a37476fea706680be139d4f81b6d0f4f Mon Sep 17 00:00:00 2001 From: Hamed Shirbandi Date: Wed, 4 Oct 2023 21:45:34 +0200 Subject: [PATCH 04/10] refactor: add preconfigured grpc to building block --- .../Configuration/Grpc/GrpcConfiguration.cs | 30 +++++++++++++++++++ .../Exceptions/GrpcGlobalExceptionHandler.cs | 26 ++++++++++++++++ .../Configuration/HostingExtensions.cs | 8 ++--- .../Configuration/HostingExtensions.cs | 6 ++-- .../Configuration/HostingExtensions.cs | 6 ++-- 5 files changed, 62 insertions(+), 14 deletions(-) create mode 100644 src/1-BuildingBlocks/Web.MVC/Configuration/Grpc/GrpcConfiguration.cs create mode 100644 src/1-BuildingBlocks/Web.MVC/Exceptions/GrpcGlobalExceptionHandler.cs diff --git a/src/1-BuildingBlocks/Web.MVC/Configuration/Grpc/GrpcConfiguration.cs b/src/1-BuildingBlocks/Web.MVC/Configuration/Grpc/GrpcConfiguration.cs new file mode 100644 index 000000000..4b6f62330 --- /dev/null +++ b/src/1-BuildingBlocks/Web.MVC/Configuration/Grpc/GrpcConfiguration.cs @@ -0,0 +1,30 @@ +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using TaskoMask.BuildingBlocks.Web.MVC.Exceptions; + +namespace TaskoMask.BuildingBlocks.Web.Grpc.Configuration +{ + + /// + /// + /// + public static class GrpcConfiguration + { + + + /// + /// + /// + public static void AddGrpcPreConfigured(this IServiceCollection services) + { + if (services == null) throw new ArgumentNullException(nameof(services)); + + services.AddGrpc(options => + { + options.Interceptors.Add(); + }); + } + + + } +} diff --git a/src/1-BuildingBlocks/Web.MVC/Exceptions/GrpcGlobalExceptionHandler.cs b/src/1-BuildingBlocks/Web.MVC/Exceptions/GrpcGlobalExceptionHandler.cs new file mode 100644 index 000000000..054181578 --- /dev/null +++ b/src/1-BuildingBlocks/Web.MVC/Exceptions/GrpcGlobalExceptionHandler.cs @@ -0,0 +1,26 @@ +using Grpc.Core; +using Grpc.Core.Interceptors; + +namespace TaskoMask.BuildingBlocks.Web.MVC.Exceptions +{ + /// + /// Global exception handler for gRPC services + /// + public class GrpcGlobalExceptionHandler : Interceptor + { + public override async Task UnaryServerHandler( + TRequest request, + ServerCallContext context, + UnaryServerMethod continuation) + { + try + { + return await continuation(request, context); + } + catch (Exception exception) + { + throw new RpcException(new Status(StatusCode.Cancelled, exception.Message)); + } + } + } +} diff --git a/src/2-Services/Boards/Api/Boards.Read.Api/Configuration/HostingExtensions.cs b/src/2-Services/Boards/Api/Boards.Read.Api/Configuration/HostingExtensions.cs index f36c05e9a..4ecbe7235 100644 --- a/src/2-Services/Boards/Api/Boards.Read.Api/Configuration/HostingExtensions.cs +++ b/src/2-Services/Boards/Api/Boards.Read.Api/Configuration/HostingExtensions.cs @@ -2,10 +2,9 @@ using TaskoMask.BuildingBlocks.Web.MVC.Configuration.Serilog; using TaskoMask.BuildingBlocks.Web.MVC.Configuration; using TaskoMask.Services.Boards.Read.Api.Infrastructure.DbContext; -using TaskoMask.BuildingBlocks.Web.MVC.Exceptions; using TaskoMask.Services.Boards.Read.Api.Infrastructure.DI; using Microsoft.AspNetCore.Builder; -using Microsoft.Extensions.DependencyInjection; +using TaskoMask.BuildingBlocks.Web.Grpc.Configuration; namespace TaskoMask.Services.Boards.Read.Api.Configuration { @@ -24,10 +23,7 @@ public static WebApplication ConfigureServices(this WebApplicationBuilder builde builder.Services.AddWebApiPreConfigured(builder.Configuration); - builder.Services.AddGrpc(options => - { - options.Interceptors.Add(); - }); + builder.Services.AddGrpcPreConfigured(); builder.Services.AddGrpcClients(builder.Configuration); diff --git a/src/2-Services/Owners/Api/Owners.Read.Api/Configuration/HostingExtensions.cs b/src/2-Services/Owners/Api/Owners.Read.Api/Configuration/HostingExtensions.cs index c62581953..ebf71ed94 100644 --- a/src/2-Services/Owners/Api/Owners.Read.Api/Configuration/HostingExtensions.cs +++ b/src/2-Services/Owners/Api/Owners.Read.Api/Configuration/HostingExtensions.cs @@ -6,6 +6,7 @@ using Microsoft.Extensions.DependencyInjection; using TaskoMask.BuildingBlocks.Web.MVC.Exceptions; using TaskoMask.Services.Owners.Read.Api.Infrastructure.DI; +using TaskoMask.BuildingBlocks.Web.Grpc.Configuration; namespace TaskoMask.Services.Owners.Read.Api.Configuration { @@ -24,10 +25,7 @@ public static WebApplication ConfigureServices(this WebApplicationBuilder builde builder.Services.AddWebApiPreConfigured(builder.Configuration); - builder.Services.AddGrpc(options => - { - options.Interceptors.Add(); - }); + builder.Services.AddGrpcPreConfigured(); return builder.Build(); } diff --git a/src/2-Services/Tasks/Api/Tasks.Read.Api/Configuration/HostingExtensions.cs b/src/2-Services/Tasks/Api/Tasks.Read.Api/Configuration/HostingExtensions.cs index 0438ee0cf..966de0029 100644 --- a/src/2-Services/Tasks/Api/Tasks.Read.Api/Configuration/HostingExtensions.cs +++ b/src/2-Services/Tasks/Api/Tasks.Read.Api/Configuration/HostingExtensions.cs @@ -6,6 +6,7 @@ using TaskoMask.Services.Tasks.Read.Api.Infrastructure.DI; using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.DependencyInjection; +using TaskoMask.BuildingBlocks.Web.Grpc.Configuration; namespace TaskoMask.Services.Tasks.Read.Api.Configuration { @@ -24,10 +25,7 @@ public static WebApplication ConfigureServices(this WebApplicationBuilder builde builder.Services.AddWebApiPreConfigured(builder.Configuration); - builder.Services.AddGrpc(options => - { - options.Interceptors.Add(); - }); + builder.Services.AddGrpcPreConfigured(); builder.Services.AddGrpcClients(builder.Configuration); From fad8ad617af30901ff7814a621b3c4d361161f86 Mon Sep 17 00:00:00 2001 From: Hamed Shirbandi Date: Wed, 4 Oct 2023 21:46:45 +0200 Subject: [PATCH 05/10] refactor: rename consumer exception model --- .../Exceptions/GrpcExceptionInterceptor.cs | 26 ------------------- ...on.cs => MessageConsumerFaultException.cs} | 4 +-- .../Consumers/OwnerProfileUpdatedConsumer.cs | 2 +- .../Consumers/OwnerRegisteredConsumer.cs | 2 +- 4 files changed, 4 insertions(+), 30 deletions(-) delete mode 100644 src/1-BuildingBlocks/Web.MVC/Exceptions/GrpcExceptionInterceptor.cs rename src/1-BuildingBlocks/Web.MVC/Exceptions/{ConsumerFaultException.cs => MessageConsumerFaultException.cs} (53%) diff --git a/src/1-BuildingBlocks/Web.MVC/Exceptions/GrpcExceptionInterceptor.cs b/src/1-BuildingBlocks/Web.MVC/Exceptions/GrpcExceptionInterceptor.cs deleted file mode 100644 index 03e748557..000000000 --- a/src/1-BuildingBlocks/Web.MVC/Exceptions/GrpcExceptionInterceptor.cs +++ /dev/null @@ -1,26 +0,0 @@ -using Grpc.Core; -using Grpc.Core.Interceptors; - -namespace TaskoMask.BuildingBlocks.Web.MVC.Exceptions -{ - /// - /// Global exception handler for gRPC services - /// - public class GrpcExceptionInterceptor : Interceptor - { - public override async Task UnaryServerHandler( - TRequest request, - ServerCallContext context, - UnaryServerMethod continuation) - { - try - { - return await continuation(request, context); - } - catch (Exception exception) - { - throw new RpcException(new Status(StatusCode.Cancelled, exception.Message)); - } - } - } -} diff --git a/src/1-BuildingBlocks/Web.MVC/Exceptions/ConsumerFaultException.cs b/src/1-BuildingBlocks/Web.MVC/Exceptions/MessageConsumerFaultException.cs similarity index 53% rename from src/1-BuildingBlocks/Web.MVC/Exceptions/ConsumerFaultException.cs rename to src/1-BuildingBlocks/Web.MVC/Exceptions/MessageConsumerFaultException.cs index 5797bf014..1cd25ccb9 100644 --- a/src/1-BuildingBlocks/Web.MVC/Exceptions/ConsumerFaultException.cs +++ b/src/1-BuildingBlocks/Web.MVC/Exceptions/MessageConsumerFaultException.cs @@ -4,9 +4,9 @@ namespace TaskoMask.BuildingBlocks.Web.MVC.Exceptions /// /// /// - public class ConsumerFaultException : Exception + public class MessageConsumerFaultException : Exception { - public ConsumerFaultException(string message): base(message) + public MessageConsumerFaultException(string message): base(message) { } diff --git a/src/2-Services/Identity/Api/Identity.Api/Consumers/OwnerProfileUpdatedConsumer.cs b/src/2-Services/Identity/Api/Identity.Api/Consumers/OwnerProfileUpdatedConsumer.cs index d94b86e85..545835b3a 100644 --- a/src/2-Services/Identity/Api/Identity.Api/Consumers/OwnerProfileUpdatedConsumer.cs +++ b/src/2-Services/Identity/Api/Identity.Api/Consumers/OwnerProfileUpdatedConsumer.cs @@ -25,7 +25,7 @@ public override async Task ConsumeMessage(ConsumeContext co var registerUser = new UpdateUserRequest(context.Message.OldEmail, context.Message.NewEmail); var result = await _inMemoryBus.SendCommand(registerUser); if (!result.IsSuccess) - throw new ConsumerFaultException(result.Message); // Cause to publish Fault message + throw new MessageConsumerFaultException(result.Message); // Cause to publish Fault message } } } diff --git a/src/2-Services/Identity/Api/Identity.Api/Consumers/OwnerRegisteredConsumer.cs b/src/2-Services/Identity/Api/Identity.Api/Consumers/OwnerRegisteredConsumer.cs index 73d56dcdb..966ed9c7b 100644 --- a/src/2-Services/Identity/Api/Identity.Api/Consumers/OwnerRegisteredConsumer.cs +++ b/src/2-Services/Identity/Api/Identity.Api/Consumers/OwnerRegisteredConsumer.cs @@ -25,7 +25,7 @@ public override async Task ConsumeMessage(ConsumeContext contex var registerUser = new RegisterUserRequest(context.Message.Id, context.Message.Email, context.Message.Password); var result = await _inMemoryBus.SendCommand(registerUser); if (!result.IsSuccess) - throw new ConsumerFaultException(result.Message); // Cause to publish Fault message + throw new MessageConsumerFaultException(result.Message); // Cause to publish Fault message } } } From ef259a5e0cc389afef324b6461640dc81a9a978a Mon Sep 17 00:00:00 2001 From: Hamed Shirbandi Date: Wed, 4 Oct 2023 21:47:08 +0200 Subject: [PATCH 06/10] feat: add a global http exception handler --- .../Configuration/MVC/WebApiConfiguration.cs | 17 +++++++-- .../Exceptions/HttpGlobalExceptionHandler.cs | 38 +++++++++++++++++++ 2 files changed, 52 insertions(+), 3 deletions(-) create mode 100644 src/1-BuildingBlocks/Web.MVC/Exceptions/HttpGlobalExceptionHandler.cs diff --git a/src/1-BuildingBlocks/Web.MVC/Configuration/MVC/WebApiConfiguration.cs b/src/1-BuildingBlocks/Web.MVC/Configuration/MVC/WebApiConfiguration.cs index e35627f3c..38869116f 100644 --- a/src/1-BuildingBlocks/Web.MVC/Configuration/MVC/WebApiConfiguration.cs +++ b/src/1-BuildingBlocks/Web.MVC/Configuration/MVC/WebApiConfiguration.cs @@ -1,9 +1,9 @@ using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Server.Kestrel.Core; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; +using MonoApi.Services.Afrr.Activations.Api.Helpers; using TaskoMask.BuildingBlocks.Web.MVC.Configuration.Jwt; using TaskoMask.BuildingBlocks.Web.MVC.Configuration.Swagger; using TaskoMask.BuildingBlocks.Web.MVC.Services.AuthenticatedUser; @@ -55,6 +55,9 @@ public static void UseWebApiPreConfigured(this IApplicationBuilder app, IWebHost if (env.IsDevelopment()) app.UseDeveloperExceptionPage(); + app.UseGlobalExceptionHandler(); + + app.UseSwaggerPreConfigured(); app.UseHttpsRedirection(); @@ -76,12 +79,20 @@ public static void UseWebApiPreConfigured(this IApplicationBuilder app, IWebHost /// /// Prevent auto validate on model binding /// - private static IMvcBuilder WithPreventAutoValidation(this IMvcBuilder builder) + private static void WithPreventAutoValidation(this IMvcBuilder builder) { - return builder.ConfigureApiBehaviorOptions(options => + builder.ConfigureApiBehaviorOptions(options => { options.SuppressModelStateInvalidFilter = true; }); } + + /// + /// + /// + private static void UseGlobalExceptionHandler(this IApplicationBuilder app) + { + app.UseMiddleware(); + } } } diff --git a/src/1-BuildingBlocks/Web.MVC/Exceptions/HttpGlobalExceptionHandler.cs b/src/1-BuildingBlocks/Web.MVC/Exceptions/HttpGlobalExceptionHandler.cs new file mode 100644 index 000000000..819decf88 --- /dev/null +++ b/src/1-BuildingBlocks/Web.MVC/Exceptions/HttpGlobalExceptionHandler.cs @@ -0,0 +1,38 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Logging; +using System.Net; +using TaskoMask.BuildingBlocks.Contracts.Helpers; + +namespace MonoApi.Services.Afrr.Activations.Api.Helpers; + +public class HttpGlobalExceptionHandler : IMiddleware +{ + private readonly ILogger _logger; + + public HttpGlobalExceptionHandler(ILogger logger) + { + this._logger = logger; + } + + public async Task InvokeAsync(HttpContext context, RequestDelegate next) + { + try + { + await next(context); + } + catch (Exception exception) + { + _logger.LogError(exception, exception.Message); + + await HandleExceptionAsync(context); + } + } + + private static async Task HandleExceptionAsync(HttpContext httpContext) + { + httpContext.Response.ContentType = "application/json"; + httpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError; + var result = Result.Failure(); + await httpContext.Response.WriteAsJsonAsync(result); + } +} From 20b9de6b76982e5bcc382747b1a14af6faaee44f Mon Sep 17 00:00:00 2001 From: Hamed Shirbandi Date: Wed, 4 Oct 2023 21:55:37 +0200 Subject: [PATCH 07/10] fix: log error in grpc global exception handler --- .../Web.MVC/Exceptions/GrpcGlobalExceptionHandler.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/1-BuildingBlocks/Web.MVC/Exceptions/GrpcGlobalExceptionHandler.cs b/src/1-BuildingBlocks/Web.MVC/Exceptions/GrpcGlobalExceptionHandler.cs index 054181578..0a42e5dca 100644 --- a/src/1-BuildingBlocks/Web.MVC/Exceptions/GrpcGlobalExceptionHandler.cs +++ b/src/1-BuildingBlocks/Web.MVC/Exceptions/GrpcGlobalExceptionHandler.cs @@ -1,5 +1,6 @@ using Grpc.Core; using Grpc.Core.Interceptors; +using Microsoft.Extensions.Logging; namespace TaskoMask.BuildingBlocks.Web.MVC.Exceptions { @@ -8,6 +9,13 @@ namespace TaskoMask.BuildingBlocks.Web.MVC.Exceptions /// public class GrpcGlobalExceptionHandler : Interceptor { + private readonly ILogger _logger; + + public GrpcGlobalExceptionHandler(ILogger logger) + { + this._logger = logger; + } + public override async Task UnaryServerHandler( TRequest request, ServerCallContext context, @@ -19,6 +27,8 @@ public override async Task UnaryServerHandler( } catch (Exception exception) { + _logger.LogError(exception, exception.Message); + throw new RpcException(new Status(StatusCode.Cancelled, exception.Message)); } } From f37a2f2787b8ff37b98b6ba5abdeb8e68f0e85b4 Mon Sep 17 00:00:00 2001 From: Hamed Shirbandi Date: Thu, 5 Oct 2023 17:56:30 +0200 Subject: [PATCH 08/10] fix: fix a command typo --- src/6-Docker/Infrastructure.yml | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/6-Docker/Infrastructure.yml b/src/6-Docker/Infrastructure.yml index f21f37ebe..f1e44a11b 100644 --- a/src/6-Docker/Infrastructure.yml +++ b/src/6-Docker/Infrastructure.yml @@ -1,3 +1,4 @@ +--- #---------------------------------------------------------------------------------- # | # If you want to run or debug some clients or services you need to first make the | @@ -5,17 +6,16 @@ # | # Just run the below command through CLI and then the environment is ready: | # | -# ...\TaskoMask\src\6-Docker> docker-compose -f Infrastucture.yml up -d | +# ...\TaskoMask\src\6-Docker> docker-compose -f Infrastructure.yml up -d | # | -# For more informition go to releated the documentation on GitHub: | +# For more informition go to the releated documentation on GitHub: | # | # https://github.com/hamed-shirbandi/TaskoMask/wiki/Development-Setup | #---------------------------------------------------------------------------------- -version: '3.4' +version: "3.4" services: - seq: container_name: seq image: datalust/seq @@ -42,15 +42,15 @@ services: ports: - 27017:27017 volumes: - - mongo:/data/db + - mongo:/data/db sql: container_name: sql image: mcr.microsoft.com/mssql/server restart: unless-stopped environment: - SA_PASSWORD: "Your_password123" - ACCEPT_EULA: "Y" + SA_PASSWORD: "Your_password123" + ACCEPT_EULA: "Y" ports: - "1433:1433" volumes: @@ -64,7 +64,7 @@ services: - "5672:5672" - "15672:15672" volumes: - - rabbitmq:/var/lib/rabbitmq/mnesia/ + - rabbitmq:/var/lib/rabbitmq/mnesia/ volumes: redis: @@ -72,4 +72,3 @@ volumes: seq: sql: rabbitmq: - From fb2a500330999fa2205688c3936c3c9a82256eb0 Mon Sep 17 00:00:00 2001 From: Hamed Shirbandi Date: Thu, 5 Oct 2023 20:40:50 +0200 Subject: [PATCH 09/10] fix: sql docker access denied error --- .gitignore | 1 + src/6-Docker/Infrastructure.yml | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index e98952029..37731856c 100644 --- a/.gitignore +++ b/.gitignore @@ -672,3 +672,4 @@ fabric.properties **/wwwroot/bundled ocelot.json /src/2-Services/Identity/Api/Identity.Api/keys +/src/6-Docker/mssqldata/Entropy.bin diff --git a/src/6-Docker/Infrastructure.yml b/src/6-Docker/Infrastructure.yml index f1e44a11b..dbd99eea5 100644 --- a/src/6-Docker/Infrastructure.yml +++ b/src/6-Docker/Infrastructure.yml @@ -25,6 +25,8 @@ services: - 5341:5341 volumes: - seq:/data + environment: + ACCEPT_EULA: "Y" redis: container_name: redis @@ -48,8 +50,9 @@ services: container_name: sql image: mcr.microsoft.com/mssql/server restart: unless-stopped + user: root environment: - SA_PASSWORD: "Your_password123" + SA_PASSWORD: "Your_pass_!@2#33" ACCEPT_EULA: "Y" ports: - "1433:1433" From aba81cd2663261ddc6879cb43ccada883cd8a6c3 Mon Sep 17 00:00:00 2001 From: Hamed Shirbandi Date: Thu, 5 Oct 2023 20:42:44 +0200 Subject: [PATCH 10/10] fix: logger service runtime bug --- .../Application/Exceptions/ManagedExceptionHandler.cs | 4 ++-- .../Application/Exceptions/UnmanagedExceptionHandler.cs | 4 ++-- .../Web.MVC/Exceptions/HttpGlobalExceptionHandler.cs | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/1-BuildingBlocks/Application/Exceptions/ManagedExceptionHandler.cs b/src/1-BuildingBlocks/Application/Exceptions/ManagedExceptionHandler.cs index 87710acd2..2d71c15c3 100644 --- a/src/1-BuildingBlocks/Application/Exceptions/ManagedExceptionHandler.cs +++ b/src/1-BuildingBlocks/Application/Exceptions/ManagedExceptionHandler.cs @@ -18,14 +18,14 @@ public class ManagedExceptionHandler private readonly INotificationHandler _notifications; - private readonly ILogger _logger; + private readonly ILogger> _logger; #endregion #region Ctors - public ManagedExceptionHandler(INotificationHandler notifications, ILogger logger) + public ManagedExceptionHandler(INotificationHandler notifications, ILogger> logger) { _notifications = notifications; this._logger = logger; diff --git a/src/1-BuildingBlocks/Application/Exceptions/UnmanagedExceptionHandler.cs b/src/1-BuildingBlocks/Application/Exceptions/UnmanagedExceptionHandler.cs index 726d4eb8b..891f410fd 100644 --- a/src/1-BuildingBlocks/Application/Exceptions/UnmanagedExceptionHandler.cs +++ b/src/1-BuildingBlocks/Application/Exceptions/UnmanagedExceptionHandler.cs @@ -18,14 +18,14 @@ public class UnmanagedExceptionHandler private readonly INotificationHandler _notifications; - private readonly ILogger _logger; + private readonly ILogger> _logger; #endregion #region Ctors - public UnmanagedExceptionHandler(INotificationHandler notifications, ILogger logger) + public UnmanagedExceptionHandler(INotificationHandler notifications, ILogger> logger) { _notifications = notifications; this._logger = logger; diff --git a/src/1-BuildingBlocks/Web.MVC/Exceptions/HttpGlobalExceptionHandler.cs b/src/1-BuildingBlocks/Web.MVC/Exceptions/HttpGlobalExceptionHandler.cs index 819decf88..8ac3446de 100644 --- a/src/1-BuildingBlocks/Web.MVC/Exceptions/HttpGlobalExceptionHandler.cs +++ b/src/1-BuildingBlocks/Web.MVC/Exceptions/HttpGlobalExceptionHandler.cs @@ -7,9 +7,9 @@ namespace MonoApi.Services.Afrr.Activations.Api.Helpers; public class HttpGlobalExceptionHandler : IMiddleware { - private readonly ILogger _logger; + private readonly ILogger _logger; - public HttpGlobalExceptionHandler(ILogger logger) + public HttpGlobalExceptionHandler(ILogger logger) { this._logger = logger; }