Skip to content

Commit f8d354a

Browse files
committed
Capture target from delegates and MethodInfo
1 parent 7a1a1eb commit f8d354a

File tree

2 files changed

+84
-9
lines changed

2 files changed

+84
-9
lines changed

src/Http/Http.Extensions/src/RequestDelegateFactory.cs

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,9 @@ public static RequestDelegateResult Create(Delegate handler, RequestDelegateFact
121121

122122
var factoryContext = CreateFactoryContext(options);
123123

124-
var targetableRequestDelegate = CreateTargetableRequestDelegate(handler.Method, targetExpression, factoryContext);
124+
Expression<Func<HttpContext, object?>> targetFactory = (httpContext) => handler.Target;
125+
126+
var targetableRequestDelegate = CreateTargetableRequestDelegate(handler.Method, targetExpression, factoryContext, targetFactory);
125127

126128
return new RequestDelegateResult(httpContext => targetableRequestDelegate(handler.Target, httpContext), factoryContext.Metadata);
127129
}
@@ -162,7 +164,7 @@ public static RequestDelegateResult Create(MethodInfo methodInfo, Func<HttpConte
162164
}
163165

164166
var targetExpression = Expression.Convert(TargetExpr, methodInfo.DeclaringType);
165-
var targetableRequestDelegate = CreateTargetableRequestDelegate(methodInfo, targetExpression, factoryContext);
167+
var targetableRequestDelegate = CreateTargetableRequestDelegate(methodInfo, targetExpression, factoryContext, context => targetFactory);
166168

167169
return new RequestDelegateResult(httpContext => targetableRequestDelegate(targetFactory(httpContext), httpContext), factoryContext.Metadata);
168170
}
@@ -187,7 +189,7 @@ private static FactoryContext CreateFactoryContext(RequestDelegateFactoryOptions
187189
return context;
188190
}
189191

190-
private static Func<object?, HttpContext, Task> CreateTargetableRequestDelegate(MethodInfo methodInfo, Expression? targetExpression, FactoryContext factoryContext)
192+
private static Func<object?, HttpContext, Task> CreateTargetableRequestDelegate(MethodInfo methodInfo, Expression? targetExpression, FactoryContext factoryContext, Expression<Func<HttpContext, object?>>? targetFactory = null)
191193
{
192194
// Non void return type
193195

@@ -223,7 +225,7 @@ private static FactoryContext CreateFactoryContext(RequestDelegateFactoryOptions
223225
// return type associated with the request to allow for the filter invocation pipeline.
224226
if (factoryContext.Filters is { Count: > 0 })
225227
{
226-
var filterPipeline = CreateFilterPipeline(methodInfo, targetExpression, factoryContext);
228+
var filterPipeline = CreateFilterPipeline(methodInfo, targetExpression, factoryContext, targetFactory);
227229
Expression<Func<RouteHandlerInvocationContext, ValueTask<object?>>> invokePipeline = (context) => filterPipeline(context);
228230
returnType = typeof(ValueTask<object?>);
229231
// var filterContext = new RouteHandlerInvocationContext(httpContext, new[] { (object)name_local, (object)int_local });
@@ -250,22 +252,28 @@ private static FactoryContext CreateFactoryContext(RequestDelegateFactoryOptions
250252
return HandleRequestBodyAndCompileRequestDelegate(responseWritingMethodCall, factoryContext);
251253
}
252254

253-
private static RouteHandlerFilterDelegate CreateFilterPipeline(MethodInfo methodInfo, Expression? target, FactoryContext factoryContext)
255+
private static RouteHandlerFilterDelegate CreateFilterPipeline(MethodInfo methodInfo, Expression? targetExpression, FactoryContext factoryContext, Expression<Func<HttpContext, object?>>? targetFactory)
254256
{
255257
Debug.Assert(factoryContext.Filters is not null);
256258
// httpContext.Response.StatusCode >= 400
257259
// ? Task.CompletedTask
258-
// : handler((string)context.Parameters[0], (int)context.Parameters[1])
260+
// : {
261+
// target = targetFactory(httpContext);
262+
// handler((string)context.Parameters[0], (int)context.Parameters[1]);
263+
// }
259264
var filteredInvocation = Expression.Lambda<RouteHandlerFilterDelegate>(
260265
Expression.Condition(
261266
Expression.GreaterThanOrEqual(FilterContextHttpContextStatusCodeExpr, Expression.Constant(400)),
262267
CompletedValueTaskExpr,
263268
Expression.Block(
264-
new[] { TargetExpr },
269+
new[] { TargetExpr, HttpContextExpr },
270+
targetFactory == null
271+
? Expression.Empty()
272+
: Expression.Assign(TargetExpr, Expression.Invoke(targetFactory, HttpContextExpr)),
265273
Expression.Call(WrapObjectAsValueTaskMethod,
266-
target is null
274+
targetExpression is null
267275
? Expression.Call(methodInfo, factoryContext.ContextArgAccess)
268-
: Expression.Call(target, methodInfo, factoryContext.ContextArgAccess))
276+
: Expression.Call(targetExpression, methodInfo, factoryContext.ContextArgAccess))
269277
)),
270278
FilterContextExpr).Compile();
271279
var routeHandlerContext = new RouteHandlerContext(

src/Http/Http.Extensions/test/RequestDelegateFactoryTests.cs

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4251,6 +4251,73 @@ string HelloName(string name)
42514251
Assert.Equal(400, httpContext.Response.StatusCode);
42524252
}
42534253

4254+
[Fact]
4255+
public async Task RequestDelegateFactory_InvokesFilters_OnDelegateWithTarget()
4256+
{
4257+
// Arrange
4258+
var httpContext = CreateHttpContext();
4259+
httpContext.Request.Query = new QueryCollection(new Dictionary<string, StringValues>
4260+
{
4261+
["name"] = "TestName"
4262+
});
4263+
4264+
// Act
4265+
var factoryResult = RequestDelegateFactory.Create((string name) => "Hello, {name}!", new RequestDelegateFactoryOptions()
4266+
{
4267+
RouteHandlerFilterFactories = new List<Func<RouteHandlerContext, RouteHandlerFilterDelegate, RouteHandlerFilterDelegate>>()
4268+
{
4269+
(routeHandlerContext, next) => async (context) =>
4270+
{
4271+
Console.WriteLine(context.Parameters[0]);
4272+
return await next(context);
4273+
}
4274+
}
4275+
});
4276+
var requestDelegate = factoryResult.RequestDelegate;
4277+
await requestDelegate(httpContext);
4278+
4279+
// Assert
4280+
Assert.Equal(200, httpContext.Response.StatusCode);
4281+
}
4282+
4283+
string GetString(string name)
4284+
{
4285+
return $"Hello, {name}!";
4286+
}
4287+
4288+
[Fact]
4289+
public async Task RequestDelegateFactory_InvokesFilters_OnMethodInfoWithTargetFactory()
4290+
{
4291+
// Arrange
4292+
var methodInfo = typeof(RequestDelegateFactoryTests).GetMethod(
4293+
nameof(GetString),
4294+
BindingFlags.NonPublic | BindingFlags.Instance,
4295+
new[] { typeof(string) });
4296+
var httpContext = CreateHttpContext();
4297+
httpContext.Request.Query = new QueryCollection(new Dictionary<string, StringValues>
4298+
{
4299+
["name"] = "TestName"
4300+
});
4301+
4302+
// Act
4303+
var factoryResult = RequestDelegateFactory.Create(methodInfo!, null, new RequestDelegateFactoryOptions()
4304+
{
4305+
RouteHandlerFilterFactories = new List<Func<RouteHandlerContext, RouteHandlerFilterDelegate, RouteHandlerFilterDelegate>>()
4306+
{
4307+
(routeHandlerContext, next) => (context) =>
4308+
{
4309+
Console.WriteLine(context.Parameters[0]);
4310+
return ValueTask.FromResult<object?>(Results.Created("/foo", "Great!"));
4311+
}
4312+
}
4313+
});
4314+
var requestDelegate = factoryResult.RequestDelegate;
4315+
await requestDelegate(httpContext);
4316+
4317+
// Assert
4318+
Assert.Equal(201, httpContext.Response.StatusCode);
4319+
}
4320+
42544321
[Fact]
42554322
public async Task RequestDelegateFactory_CanInvokeSingleEndpointFilter_ThatProvidesCustomErrorMessage()
42564323
{

0 commit comments

Comments
 (0)