From eae5a0ad9d63317f2e23f73adffb4c401736a7f9 Mon Sep 17 00:00:00 2001 From: zhifenglee-aelf Date: Mon, 14 Oct 2024 11:15:30 +0800 Subject: [PATCH] feat(exception handler): removed dependencies --- .../AElf.ExceptionHandler.csproj | 13 ++- AElf.ExceptionHandler/AOPExceptionModule.cs | 33 ------ AElf.ExceptionHandler/AttributeCallFilter.cs | 64 ----------- AElf.ExceptionHandler/ExceptionHandler.cs | 100 +++++++++++++++--- .../ExceptionHandlerAttribute.cs | 1 + .../ExceptionHandlerInterceptor.cs | 35 ------ .../ExceptionHandlingStrategy.cs | 1 + .../ExceptionHandlerConfigurationExtension.cs | 17 +++ .../ReturnTypeMismatchException.cs | 8 ++ 9 files changed, 120 insertions(+), 152 deletions(-) delete mode 100644 AElf.ExceptionHandler/AOPExceptionModule.cs delete mode 100644 AElf.ExceptionHandler/AttributeCallFilter.cs delete mode 100644 AElf.ExceptionHandler/ExceptionHandlerInterceptor.cs create mode 100644 AElf.ExceptionHandler/Extensions/ExceptionHandlerConfigurationExtension.cs create mode 100644 AElf.ExceptionHandler/ReturnTypeMismatchException.cs diff --git a/AElf.ExceptionHandler/AElf.ExceptionHandler.csproj b/AElf.ExceptionHandler/AElf.ExceptionHandler.csproj index 1d7ad8c..f02499e 100644 --- a/AElf.ExceptionHandler/AElf.ExceptionHandler.csproj +++ b/AElf.ExceptionHandler/AElf.ExceptionHandler.csproj @@ -5,8 +5,8 @@ AElf.ExceptionHandler AElf ExceptionHandler AElf - An ExceptionHandler module for use in ABP and Orleans framework. - abp;module;grains;orleans + An ExceptionHandler in AOP. + aop net8.0 enable enable @@ -16,15 +16,14 @@ AElf.ExceptionHandler - - - - - README.md LICENSE + + + + diff --git a/AElf.ExceptionHandler/AOPExceptionModule.cs b/AElf.ExceptionHandler/AOPExceptionModule.cs deleted file mode 100644 index 509d71d..0000000 --- a/AElf.ExceptionHandler/AOPExceptionModule.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System.Collections.Concurrent; -using Microsoft.Extensions.DependencyInjection; -using Orleans; -using Volo.Abp.Modularity; - -namespace AElf.ExceptionHandler; - -public class AOPExceptionModule : AbpModule -{ - public override void ConfigureServices(ServiceConfigurationContext context) - { - context.Services.AddTransient() - .AddTransient() - .AddSingleton>() - .AddSingleton>>() - .AddSingleton>>() - .AddSingleton(); - - context.Services.OnRegistered(options => - { - var methodInfos = options.ImplementationType.GetMethods(); - // Check if any of the class methods is decorated with the ExceptionHandlerAttribute - foreach (var methodInfo in methodInfos) - { - if (methodInfo.IsDefined(typeof(ExceptionHandlerAttribute), true)) - { - var result = options.Interceptors.TryAdd(); - break; - } - } - }); - } -} \ No newline at end of file diff --git a/AElf.ExceptionHandler/AttributeCallFilter.cs b/AElf.ExceptionHandler/AttributeCallFilter.cs deleted file mode 100644 index 50f944c..0000000 --- a/AElf.ExceptionHandler/AttributeCallFilter.cs +++ /dev/null @@ -1,64 +0,0 @@ -using Microsoft.Extensions.DependencyInjection; -using Orleans; -using Orleans.Serialization.Invocation; - -namespace AElf.ExceptionHandler; - -public class AttributeCallFilter : IIncomingGrainCallFilter -{ - public async Task Invoke(IIncomingGrainCallContext context) - { - if (context.ImplementationMethod.IsDefined(typeof(ExceptionHandlerAttribute), true)) - { - var exceptionHandler = context.TargetContext.ActivationServices.GetService(); - - if (exceptionHandler == null) - { - throw new Exception("ExceptionHandler is not registered."); - } - - var arguments = new object?[context.Request.GetArgumentCount()]; - for (var i = 0; i < context.Request.GetArgumentCount(); ++i) - { - arguments[i] = context.Request.GetArgument(i); - } - - var methodExecutionArgs = new MethodExecutionArgs - { - TargetObject = context.Grain, - MethodInfo = context.ImplementationMethod, - Arguments = arguments!, - ReturnValue = null, - Exception = null, - Invocation = async () => - { - await context.Invoke(); - } - }; - - await exceptionHandler.InterceptAsync(methodExecutionArgs); - - if (methodExecutionArgs.Exception != null) - { - context.Response = new ExceptionResponse - { - Exception = methodExecutionArgs.Exception - }; - } - else - { - if (methodExecutionArgs.ReturnValue != null) - { - var responseType = typeof(Response<>).MakeGenericType(methodExecutionArgs.ReturnValue.GetType()); - var response = Activator.CreateInstance(responseType); - responseType.GetProperty("Result")?.SetValue(response, methodExecutionArgs.ReturnValue); - context.Response = (Response)response!; - } - } - } - else - { - await context.Invoke(); - } - } -} \ No newline at end of file diff --git a/AElf.ExceptionHandler/ExceptionHandler.cs b/AElf.ExceptionHandler/ExceptionHandler.cs index 1b8ea92..98bf717 100644 --- a/AElf.ExceptionHandler/ExceptionHandler.cs +++ b/AElf.ExceptionHandler/ExceptionHandler.cs @@ -2,11 +2,10 @@ using System.Linq.Expressions; using System.Reflection; using Microsoft.Extensions.Logging; -using Volo.Abp.DependencyInjection; namespace AElf.ExceptionHandler; -public class ExceptionHandler : ITransientDependency, IInterceptor +public class ExceptionHandler : IInterceptor { private class Result { @@ -174,14 +173,7 @@ private Result HandleInnerException(Exception exception, MethodExecutionArgs arg // Log the exception if(logger != null) { - if (attribute.Message != null) - { - ((ILogger)logger).Log(attribute.LogLevel, exception, "Message: {Message} Exception Message: {ExceptionMessage}", attribute.Message, exception.Message); - } - else - { - ((ILogger)logger).Log(attribute.LogLevel, exception, "Exception Message: {ExceptionMessage}", exception.Message); - } + LogException(exception, args, attribute, logger); } if (attribute.LogOnly) @@ -201,7 +193,7 @@ private Result HandleInnerException(Exception exception, MethodExecutionArgs arg if (returnType.IsGenericType && returnType.GetGenericTypeDefinition() == typeof(Task<>)) { - genericType = returnType.GetGenericArguments()[0]; + genericType = returnType.GetGenericArguments().FirstOrDefault(); } if (genericType != null) @@ -218,6 +210,13 @@ private Result HandleInnerException(Exception exception, MethodExecutionArgs arg Rethrow = false }; } + + // if the method returns Task and not Task<> + return new Result + { + Handled = true, + Rethrow = false + }; } var exceptionHandlerInfo = GetExceptionHandlerInfo(attribute.TargetType, attribute.MethodName); @@ -246,6 +245,16 @@ private Result HandleInnerException(Exception exception, MethodExecutionArgs arg Rethrow = true }; } + + if(flowBehavior.ExceptionHandlingStrategy == ExceptionHandlingStrategy.Continue) + { + args.Exception = exception; + return new Result + { + Handled = false, + Rethrow = false + }; + } if (flowBehavior.ReturnValue == null) { @@ -258,20 +267,85 @@ private Result HandleInnerException(Exception exception, MethodExecutionArgs arg if(flowBehavior.ExceptionHandlingStrategy == ExceptionHandlingStrategy.Throw) { + if (args.MethodInfo.ReturnType != typeof(Exception)) + { + throw new ReturnTypeMismatchException("Return type mismatch when trying to throw a new exception. ReturnValue should be of type Exception."); + } var newException = (Exception)flowBehavior.ReturnValue!; args.Exception = newException; throw newException; } args.Exception = null; - args.ReturnValue = flowBehavior.ReturnValue; + + //throw custom exception if the return is different + var flowReturnType = flowBehavior.ReturnValue.GetType(); + Type? genericReturnType = null; + + if (args.MethodInfo.ReturnType.IsGenericType && args.MethodInfo.ReturnType.GetGenericTypeDefinition() == typeof(Task<>)) + { + genericReturnType = args.MethodInfo.ReturnType.GetGenericArguments().FirstOrDefault(); + } + + if (genericReturnType != null) + { + if (genericReturnType != flowReturnType) + { + throw new ReturnTypeMismatchException( + $"Return type mismatch when handling exception's return value. ReturnValue should be of type {genericReturnType} but was {flowReturnType}."); + } + + args.ReturnValue = flowBehavior.ReturnValue; + } + return new Result { Handled = true, Rethrow = false }; } - + + private static void LogException(Exception exception, MethodExecutionArgs args, ExceptionHandlerAttribute attribute, + object logger) + { + var logValues = new List { exception.Message }; + var logMessage = $"Exception Message: {{ExceptionMessage}}"; + //get parameter names from args.MethodInfo and check if it exists within attribute.LogTargets. if it does, log it + if (attribute.LogTargets != null) + { + var parameters = args.MethodInfo.GetParameters(); + var parameterNames = parameters.Select(p => p.Name).ToArray(); + var logTargets = attribute.LogTargets.Intersect(parameterNames).ToArray(); + if (logTargets.Length > 0) + { + foreach (var logTarget in logTargets) + { + // check if the parameter is a value type or a reference type + var parameter = parameters.First(p => p.Name == logTarget); + if (parameter.ParameterType.IsValueType || parameter.ParameterType == typeof(string)) + { + logMessage += $" {logTarget}: {{{logTarget}}}"; + } + else + { + logMessage += $" {logTarget}: {{@{logTarget}}}"; + } + } + logValues.AddRange(logTargets.Select(target => args.Arguments[Array.IndexOf(parameterNames, target)])); + } + } + if (attribute.Message != null) + { + logMessage = $"Message: {{Message}} " + logMessage; + logValues.Insert(0, attribute.Message); + ((ILogger)logger).Log(attribute.LogLevel, exception, logMessage, logValues.ToArray()); + } + else + { + ((ILogger)logger).Log(attribute.LogLevel, exception, logMessage, logValues.ToArray()); + } + } + private ExceptionHandlerInfo GetExceptionHandlerInfo(Type targetType, string methodName) { var cacheKey = CacheKey(targetType, methodName); diff --git a/AElf.ExceptionHandler/ExceptionHandlerAttribute.cs b/AElf.ExceptionHandler/ExceptionHandlerAttribute.cs index 5525ed0..2ea7480 100644 --- a/AElf.ExceptionHandler/ExceptionHandlerAttribute.cs +++ b/AElf.ExceptionHandler/ExceptionHandlerAttribute.cs @@ -14,6 +14,7 @@ public class ExceptionHandlerAttribute : Attribute public string? FinallyMethodName { get; set; } = null; public string? Message { get; set; } = null; public ReturnDefault ReturnDefault { get; set; } = ReturnDefault.None; + public string[]? LogTargets { get; set; } = null; public ExceptionHandlerAttribute(params Type [] exceptions) { diff --git a/AElf.ExceptionHandler/ExceptionHandlerInterceptor.cs b/AElf.ExceptionHandler/ExceptionHandlerInterceptor.cs deleted file mode 100644 index c914f13..0000000 --- a/AElf.ExceptionHandler/ExceptionHandlerInterceptor.cs +++ /dev/null @@ -1,35 +0,0 @@ -using Volo.Abp.DynamicProxy; - -namespace AElf.ExceptionHandler; - -public class ExceptionHandlerInterceptor : AbpInterceptor -{ - private readonly IInterceptor _interceptor; - - public ExceptionHandlerInterceptor(IInterceptor interceptor) - { - _interceptor = interceptor; - } - - public override async Task InterceptAsync(IAbpMethodInvocation invocation) - { - var methodExecutionArgs = new MethodExecutionArgs - { - TargetObject = invocation.TargetObject, - MethodInfo = invocation.Method, - Arguments = invocation.Arguments, - ReturnValue = null, - Invocation = async () => - { - await invocation.ProceedAsync(); - } - }; - - await _interceptor.InterceptAsync(methodExecutionArgs); - - if (methodExecutionArgs.ReturnValue != null) - { - invocation.ReturnValue = methodExecutionArgs.ReturnValue; - } - } -} diff --git a/AElf.ExceptionHandler/ExceptionHandlingStrategy.cs b/AElf.ExceptionHandler/ExceptionHandlingStrategy.cs index 8d15683..7b52aa7 100644 --- a/AElf.ExceptionHandler/ExceptionHandlingStrategy.cs +++ b/AElf.ExceptionHandler/ExceptionHandlingStrategy.cs @@ -5,4 +5,5 @@ public enum ExceptionHandlingStrategy Rethrow, Return, Throw, + Continue } \ No newline at end of file diff --git a/AElf.ExceptionHandler/Extensions/ExceptionHandlerConfigurationExtension.cs b/AElf.ExceptionHandler/Extensions/ExceptionHandlerConfigurationExtension.cs new file mode 100644 index 0000000..f115288 --- /dev/null +++ b/AElf.ExceptionHandler/Extensions/ExceptionHandlerConfigurationExtension.cs @@ -0,0 +1,17 @@ +using System.Collections.Concurrent; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; + +namespace AElf.ExceptionHandler.Extensions; + +public static class ExceptionHandlerConfigurationExtension +{ + public static IServiceCollection AddExceptionHandler(this IServiceCollection services) + { + services.TryAddTransient(); + services.TryAddSingleton>(); + services.TryAddSingleton>>(); + services.TryAddSingleton>>(); + return services; + } +} \ No newline at end of file diff --git a/AElf.ExceptionHandler/ReturnTypeMismatchException.cs b/AElf.ExceptionHandler/ReturnTypeMismatchException.cs new file mode 100644 index 0000000..c86117b --- /dev/null +++ b/AElf.ExceptionHandler/ReturnTypeMismatchException.cs @@ -0,0 +1,8 @@ +namespace AElf.ExceptionHandler; + +public class ReturnTypeMismatchException : Exception +{ + public ReturnTypeMismatchException(string message) : base(message) + { + } +} \ No newline at end of file