diff --git a/src/MagicOnion.Server/Filters/Internal/FilterHelper.cs b/src/MagicOnion.Server/Filters/Internal/FilterHelper.cs index 0fd07536d..834f52930 100644 --- a/src/MagicOnion.Server/Filters/Internal/FilterHelper.cs +++ b/src/MagicOnion.Server/Filters/Internal/FilterHelper.cs @@ -1,3 +1,4 @@ +using System.Diagnostics; using System.Reflection; using MagicOnion.Server.Hubs; @@ -69,28 +70,30 @@ IMagicOnionFilterFactory filterFactory public static Func WrapMethodBodyWithFilter(IServiceProvider serviceProvider, IEnumerable filters, Func methodBody) { - Func next = methodBody; + Func outer = methodBody; foreach (var filterDescriptor in filters.Reverse()) { var newFilter = CreateOrGetInstance(serviceProvider, filterDescriptor); - next = new InvokeHelper>(newFilter.Invoke, next).GetDelegate(); + var inner = outer; + outer = [StackTraceHidden] (ctx) => newFilter.Invoke(ctx, inner); } - return next; + return outer; } public static Func WrapMethodBodyWithFilter(IServiceProvider serviceProvider, IEnumerable filters, Func methodBody) { - Func next = methodBody; + Func outer = methodBody; foreach (var filterDescriptor in filters.Reverse()) { var newFilter = CreateOrGetInstance(serviceProvider, filterDescriptor); - next = new InvokeHelper>(newFilter.Invoke, next).GetDelegate(); + var inner = outer; + outer = [StackTraceHidden] (ctx) => newFilter.Invoke(ctx, inner); } - return next; + return outer; } public static TFilter CreateOrGetInstance(IServiceProvider serviceProvider, MagicOnionFilterDescriptor descriptor) diff --git a/tests/MagicOnion.Server.Tests/Filter/FilterHelperTest.cs b/tests/MagicOnion.Server.Tests/Filter/FilterHelperTest.cs index 91ffc6372..aab56acf8 100644 --- a/tests/MagicOnion.Server.Tests/Filter/FilterHelperTest.cs +++ b/tests/MagicOnion.Server.Tests/Filter/FilterHelperTest.cs @@ -745,6 +745,39 @@ public async Task WrapMethodBodyWithFilter_Surround_Service() results.Should().Equal(1, 2, 0, 200, 100); } + [Fact] + public async Task WrapMethodBodyWithFilter_Surround_Service_ThrowStackTrace() + { + // Arrange + var services = new ServiceCollection(); + var serviceProvider = services.BuildServiceProvider(); + var callStack = default(string); + var filters = new[] + { + new MagicOnionServiceFilterDescriptor(new DelegateServiceFilter(async (context, next) => + { + await next(context); + })), + new MagicOnionServiceFilterDescriptor(new DelegateServiceFilter(async (context, next) => + { + await next(context); + })), + }; + + // Act + var body = FilterHelper.WrapMethodBodyWithFilter(serviceProvider, filters, (context) => + { + callStack = Environment.StackTrace; + throw new InvalidOperationException(); + }); + var ex = await Record.ExceptionAsync(async () => await body(default)); + + // Assert + ex.Should().NotBeNull(); + ex.StackTrace.Should().NotContain("MagicOnion.Server.Filters.Internal.FilterHelper.<>c__DisplayClass"); + callStack.Should().NotContain("MagicOnion.Server.Filters.Internal.FilterHelper.<>c__DisplayClass"); + } + class DelegateServiceFilter : IMagicOnionServiceFilter { readonly Func, ValueTask> func; @@ -793,6 +826,39 @@ public async Task WrapMethodBodyWithFilter_Surround_StreamingHub() results.Should().Equal(1, 2, 0, 200, 100); } + [Fact] + public async Task WrapMethodBodyWithFilter_Surround_StreamingHub_ThrowStackTrace() + { + // Arrange + var services = new ServiceCollection(); + var serviceProvider = services.BuildServiceProvider(); + var callStack = default(string); + var filters = new[] + { + new StreamingHubFilterDescriptor(new DelegateHubFilter(async (context, next) => + { + await next(context); + })), + new StreamingHubFilterDescriptor(new DelegateHubFilter(async (context, next) => + { + await next(context); + })), + }; + + // Act + var body = FilterHelper.WrapMethodBodyWithFilter(serviceProvider, filters, (context) => + { + callStack = Environment.StackTrace; + throw new InvalidOperationException(); + }); + var ex = await Record.ExceptionAsync(async () => await body(default)); + + // Assert + ex.Should().NotBeNull(); + ex.StackTrace.Should().NotContain("MagicOnion.Server.Filters.Internal.FilterHelper.<>c__DisplayClass"); + callStack.Should().NotContain("MagicOnion.Server.Filters.Internal.FilterHelper.<>c__DisplayClass"); + } + class DelegateHubFilter : IStreamingHubFilter { readonly Func, ValueTask> func;