diff options
author | Brennan Conroy <brecon@microsoft.com> | 2022-04-15 02:39:47 +0300 |
---|---|---|
committer | Brennan Conroy <brecon@microsoft.com> | 2022-04-15 02:54:59 +0300 |
commit | 7ef19233a06f56f073bb1a9b3d174217ac243dd3 (patch) | |
tree | 438dc10f5a7205991d5ec9fab5c37819ba028ca5 | |
parent | 426949d51ed64faa97bf2138417a737996267e08 (diff) |
Fix endpoint filters with inline delegatebrecon/filternull
-rw-r--r-- | src/Http/Http.Extensions/src/RequestDelegateFactory.cs | 41 | ||||
-rw-r--r-- | src/Http/Http.Extensions/test/RequestDelegateFactoryTests.cs | 40 |
2 files changed, 68 insertions, 13 deletions
diff --git a/src/Http/Http.Extensions/src/RequestDelegateFactory.cs b/src/Http/Http.Extensions/src/RequestDelegateFactory.cs index 7b25889d0c..745a8b120e 100644 --- a/src/Http/Http.Extensions/src/RequestDelegateFactory.cs +++ b/src/Http/Http.Extensions/src/RequestDelegateFactory.cs @@ -113,7 +113,7 @@ public static partial class RequestDelegateFactory throw new ArgumentNullException(nameof(handler)); } - var targetExpression = handler.Target switch + var targetConvertMethod = handler.Target switch { object => Expression.Convert(TargetExpr, handler.Target.GetType()), null => null, @@ -121,7 +121,7 @@ public static partial class RequestDelegateFactory var factoryContext = CreateFactoryContext(options); - var targetableRequestDelegate = CreateTargetableRequestDelegate(handler.Method, targetExpression, factoryContext); + var targetableRequestDelegate = CreateTargetableRequestDelegate(handler.Method, targetConvertMethod, factoryContext, handler.Target); return new RequestDelegateResult(httpContext => targetableRequestDelegate(handler.Target, httpContext), factoryContext.Metadata); } @@ -153,7 +153,7 @@ public static partial class RequestDelegateFactory { if (methodInfo.IsStatic) { - var untargetableRequestDelegate = CreateTargetableRequestDelegate(methodInfo, targetExpression: null, factoryContext); + var untargetableRequestDelegate = CreateTargetableRequestDelegate(methodInfo, targetConvertMethod: null, factoryContext); return new RequestDelegateResult(httpContext => untargetableRequestDelegate(null, httpContext), factoryContext.Metadata); } @@ -187,7 +187,8 @@ public static partial class RequestDelegateFactory return context; } - private static Func<object?, HttpContext, Task> CreateTargetableRequestDelegate(MethodInfo methodInfo, Expression? targetExpression, FactoryContext factoryContext) + private static Func<object?, HttpContext, Task> CreateTargetableRequestDelegate(MethodInfo methodInfo, Expression? targetConvertMethod, + FactoryContext factoryContext, object? targetInstance = null) { // Non void return type @@ -211,7 +212,7 @@ public static partial class RequestDelegateFactory // CreateArguments will add metadata inferred from parameter details var arguments = CreateArguments(methodInfo.GetParameters(), factoryContext); var returnType = methodInfo.ReturnType; - factoryContext.MethodCall = CreateMethodCall(methodInfo, targetExpression, arguments); + factoryContext.MethodCall = CreateMethodCall(methodInfo, targetConvertMethod, arguments); // Add metadata provided by the delegate return type and parameter types next, this will be more specific than inferred metadata from above AddTypeProvidedMetadata(methodInfo, factoryContext.Metadata, factoryContext.ServiceProvider); @@ -223,11 +224,11 @@ public static partial class RequestDelegateFactory // return type associated with the request to allow for the filter invocation pipeline. if (factoryContext.Filters is { Count: > 0 }) { - var filterPipeline = CreateFilterPipeline(methodInfo, targetExpression, factoryContext); + var filterPipeline = CreateFilterPipeline(methodInfo, targetConvertMethod, factoryContext, targetInstance); Expression<Func<RouteHandlerInvocationContext, ValueTask<object?>>> invokePipeline = (context) => filterPipeline(context); returnType = typeof(ValueTask<object?>); // var filterContext = new RouteHandlerInvocationContext(httpContext, new[] { (object)name_local, (object)int_local }); - // invokePipeline.Invoke(filterContext); + // invokePipeline.Invoke(target, filterContext); factoryContext.MethodCall = Expression.Block( new[] { InvokedFilterContextExpr }, Expression.Assign( @@ -250,24 +251,38 @@ public static partial class RequestDelegateFactory return HandleRequestBodyAndCompileRequestDelegate(responseWritingMethodCall, factoryContext); } - private static RouteHandlerFilterDelegate CreateFilterPipeline(MethodInfo methodInfo, Expression? target, FactoryContext factoryContext) + private static RouteHandlerFilterDelegate CreateFilterPipeline(MethodInfo methodInfo, Expression? targetConvertMethod, + FactoryContext factoryContext, object? targetInstance) { Debug.Assert(factoryContext.Filters is not null); - // httpContext.Response.StatusCode >= 400 - // ? Task.CompletedTask - // : handler((string)context.Parameters[0], (int)context.Parameters[1]) + // if (httpContext.Response.StatusCode >= 400) + // { + // return Task.CompletedTask; + // } + // if (inline-delegate) // code generation time check + // { + // var target; + // target = targetInstance; + // return Convert(target, compilerType).handler((string)context.Parameters[0], (int)context.Parameters[1]); + // } + // else + // { + // return handler((string)context.Parameters[0], (int)context.Parameters[1]); + // } var filteredInvocation = Expression.Lambda<RouteHandlerFilterDelegate>( Expression.Condition( Expression.GreaterThanOrEqual(FilterContextHttpContextStatusCodeExpr, Expression.Constant(400)), CompletedValueTaskExpr, Expression.Block( new[] { TargetExpr }, + Expression.Assign(TargetExpr, Expression.Constant(targetInstance)), Expression.Call(WrapObjectAsValueTaskMethod, - target is null + targetConvertMethod is null ? Expression.Call(methodInfo, factoryContext.ContextArgAccess) - : Expression.Call(target, methodInfo, factoryContext.ContextArgAccess)) + : Expression.Call(targetConvertMethod, methodInfo, factoryContext.ContextArgAccess)) )), FilterContextExpr).Compile(); + var routeHandlerContext = new RouteHandlerContext( methodInfo, new EndpointMetadataCollection(factoryContext.Metadata)); diff --git a/src/Http/Http.Extensions/test/RequestDelegateFactoryTests.cs b/src/Http/Http.Extensions/test/RequestDelegateFactoryTests.cs index 5501077e54..6ef957f23a 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateFactoryTests.cs +++ b/src/Http/Http.Extensions/test/RequestDelegateFactoryTests.cs @@ -4579,6 +4579,46 @@ public class RequestDelegateFactoryTests : LoggedTest } [Fact] + public async Task RequestDelegateFactory_CanInvokeEndpointFilter_WithInlineDelegate() + { + // Arrange + var httpContext = CreateHttpContext(); + + var responseBodyStream = new MemoryStream(); + httpContext.Response.Body = responseBodyStream; + + httpContext.Request.Query = new QueryCollection(new Dictionary<string, StringValues> + { + ["name"] = "TestName" + }); + + // Act + var factoryResult = RequestDelegateFactory.Create((string name) => + { + return $"Hello, {name}!"; + }, + new RequestDelegateFactoryOptions() + { + RouteHandlerFilterFactories = new List<Func<RouteHandlerContext, RouteHandlerFilterDelegate, RouteHandlerFilterDelegate>>() + { + (routeHandlerContext, next) => + { + return async (context) => + { + return await next(context); + }; + }, + } + }); + var requestDelegate = factoryResult.RequestDelegate; + await requestDelegate(httpContext); + + // Assert + var responseBody = Encoding.UTF8.GetString(responseBodyStream.ToArray()); + Assert.Equal("Hello, TestName!", responseBody); + } + + [Fact] public void Create_AddsDelegateMethodInfo_AsMetadata() { // Arrange |