1111using System . Reflection ;
1212using System . Threading ;
1313using System . Threading . Tasks ;
14- using Microsoft . AspNetCore . Http ;
1514using Microsoft . AspNetCore . Http . Metadata ;
1615using Microsoft . Extensions . DependencyInjection ;
1716using Microsoft . Extensions . Internal ;
1817using Microsoft . Extensions . Logging ;
1918
20- namespace Microsoft . AspNetCore . Routing . Internal
19+ namespace Microsoft . AspNetCore . Http
2120{
22- internal static class MapActionExpressionTreeBuilder
21+ /// <summary>
22+ /// Creates <see cref="RequestDelegate"/> implementations from <see cref="Delegate"/> request handlers.
23+ /// </summary>
24+ public static class RequestDelegateFactory
2325 {
2426 private static readonly MethodInfo ChangeTypeMethodInfo = GetMethodInfo < Func < object , Type , object > > ( ( value , type ) => Convert . ChangeType ( value , type , CultureInfo . InvariantCulture ) ) ;
25- private static readonly MethodInfo ExecuteTaskOfTMethodInfo = typeof ( MapActionExpressionTreeBuilder ) . GetMethod ( nameof ( ExecuteTask ) , BindingFlags . NonPublic | BindingFlags . Static ) ! ;
26- private static readonly MethodInfo ExecuteTaskOfStringMethodInfo = typeof ( MapActionExpressionTreeBuilder ) . GetMethod ( nameof ( ExecuteTaskOfString ) , BindingFlags . NonPublic | BindingFlags . Static ) ! ;
27- private static readonly MethodInfo ExecuteValueTaskOfTMethodInfo = typeof ( MapActionExpressionTreeBuilder ) . GetMethod ( nameof ( ExecuteValueTaskOfT ) , BindingFlags . NonPublic | BindingFlags . Static ) ! ;
28- private static readonly MethodInfo ExecuteValueTaskMethodInfo = typeof ( MapActionExpressionTreeBuilder ) . GetMethod ( nameof ( ExecuteValueTask ) , BindingFlags . NonPublic | BindingFlags . Static ) ! ;
29- private static readonly MethodInfo ExecuteValueTaskOfStringMethodInfo = typeof ( MapActionExpressionTreeBuilder ) . GetMethod ( nameof ( ExecuteValueTaskOfString ) , BindingFlags . NonPublic | BindingFlags . Static ) ! ;
30- private static readonly MethodInfo ExecuteTaskResultOfTMethodInfo = typeof ( MapActionExpressionTreeBuilder ) . GetMethod ( nameof ( ExecuteTaskResult ) , BindingFlags . NonPublic | BindingFlags . Static ) ! ;
31- private static readonly MethodInfo ExecuteValueResultTaskOfTMethodInfo = typeof ( MapActionExpressionTreeBuilder ) . GetMethod ( nameof ( ExecuteValueTaskResult ) , BindingFlags . NonPublic | BindingFlags . Static ) ! ;
27+ private static readonly MethodInfo ExecuteTaskOfTMethodInfo = typeof ( RequestDelegateFactory ) . GetMethod ( nameof ( ExecuteTask ) , BindingFlags . NonPublic | BindingFlags . Static ) ! ;
28+ private static readonly MethodInfo ExecuteTaskOfStringMethodInfo = typeof ( RequestDelegateFactory ) . GetMethod ( nameof ( ExecuteTaskOfString ) , BindingFlags . NonPublic | BindingFlags . Static ) ! ;
29+ private static readonly MethodInfo ExecuteValueTaskOfTMethodInfo = typeof ( RequestDelegateFactory ) . GetMethod ( nameof ( ExecuteValueTaskOfT ) , BindingFlags . NonPublic | BindingFlags . Static ) ! ;
30+ private static readonly MethodInfo ExecuteValueTaskMethodInfo = typeof ( RequestDelegateFactory ) . GetMethod ( nameof ( ExecuteValueTask ) , BindingFlags . NonPublic | BindingFlags . Static ) ! ;
31+ private static readonly MethodInfo ExecuteValueTaskOfStringMethodInfo = typeof ( RequestDelegateFactory ) . GetMethod ( nameof ( ExecuteValueTaskOfString ) , BindingFlags . NonPublic | BindingFlags . Static ) ! ;
32+ private static readonly MethodInfo ExecuteTaskResultOfTMethodInfo = typeof ( RequestDelegateFactory ) . GetMethod ( nameof ( ExecuteTaskResult ) , BindingFlags . NonPublic | BindingFlags . Static ) ! ;
33+ private static readonly MethodInfo ExecuteValueResultTaskOfTMethodInfo = typeof ( RequestDelegateFactory ) . GetMethod ( nameof ( ExecuteValueTaskResult ) , BindingFlags . NonPublic | BindingFlags . Static ) ! ;
3234 private static readonly MethodInfo GetRequiredServiceMethodInfo = typeof ( ServiceProviderServiceExtensions ) . GetMethod ( nameof ( ServiceProviderServiceExtensions . GetRequiredService ) , BindingFlags . Public | BindingFlags . Static , new Type [ ] { typeof ( IServiceProvider ) } ) ! ;
3335 private static readonly MethodInfo ResultWriteResponseAsync = typeof ( IResult ) . GetMethod ( nameof ( IResult . ExecuteAsync ) , BindingFlags . Public | BindingFlags . Instance ) ! ;
3436 private static readonly MethodInfo StringResultWriteResponseAsync = GetMethodInfo < Func < HttpResponse , string , Task > > ( ( response , text ) => HttpResponseWritingExtensions . WriteAsync ( response , text , default ) ) ;
@@ -44,7 +46,85 @@ internal static class MapActionExpressionTreeBuilder
4446 private static readonly MemberExpression HttpResponseExpr = Expression . Property ( HttpContextParameter , nameof ( HttpContext . Response ) ) ;
4547 private static readonly MemberExpression RequestAbortedExpr = Expression . Property ( HttpContextParameter , nameof ( HttpContext . RequestAborted ) ) ;
4648
47- public static RequestDelegate BuildRequestDelegate ( Delegate action )
49+ /// <summary>
50+ /// Creates a <see cref="RequestDelegate"/> implementation for <paramref name="action"/>.
51+ /// </summary>
52+ /// <param name="action">A request handler with any number of custom parameters that often produces a response with its return value.</param>
53+ /// <returns>The <see cref="RequestDelegate"/>.</returns>
54+ public static RequestDelegate Create ( Delegate action )
55+ {
56+ if ( action is null )
57+ {
58+ throw new ArgumentNullException ( nameof ( action ) ) ;
59+ }
60+
61+ var targetExpression = action . Target switch
62+ {
63+ object => Expression . Convert ( TargetArg , action . Target . GetType ( ) ) ,
64+ null => null ,
65+ } ;
66+
67+ var untargetedRequestDelegate = CreateRequestDelegate ( action . Method , targetExpression ) ;
68+
69+ return httpContext =>
70+ {
71+ return untargetedRequestDelegate ( action . Target , httpContext ) ;
72+ } ;
73+ }
74+
75+ /// <summary>
76+ /// Creates a <see cref="RequestDelegate"/> implementation for <paramref name="methodInfo"/>.
77+ /// </summary>
78+ /// <param name="methodInfo">A static request handler with any number of custom parameters that often produces a response with its return value.</param>
79+ /// <returns>The <see cref="RequestDelegate"/>.</returns>
80+ public static RequestDelegate Create ( MethodInfo methodInfo )
81+ {
82+ if ( methodInfo is null )
83+ {
84+ throw new ArgumentNullException ( nameof ( methodInfo ) ) ;
85+ }
86+
87+ var untargetedRequestDelegate = CreateRequestDelegate ( methodInfo , targetExpression : null ) ;
88+
89+ return httpContext =>
90+ {
91+ return untargetedRequestDelegate ( null , httpContext ) ;
92+ } ;
93+ }
94+
95+ /// <summary>
96+ /// Creates a <see cref="RequestDelegate"/> implementation for <paramref name="methodInfo"/>.
97+ /// </summary>
98+ /// <param name="methodInfo">A request handler with any number of custom parameters that often produces a response with its return value.</param>
99+ /// <param name="targetFactory">Creates the <see langword="this"/> for the non-static method.</param>
100+ /// <returns>The <see cref="RequestDelegate"/>.</returns>
101+ public static RequestDelegate Create ( MethodInfo methodInfo , Func < HttpContext , object > targetFactory )
102+ {
103+ if ( methodInfo is null )
104+ {
105+ throw new ArgumentNullException ( nameof ( methodInfo ) ) ;
106+ }
107+
108+ if ( targetFactory is null )
109+ {
110+ throw new ArgumentNullException ( nameof ( targetFactory ) ) ;
111+ }
112+
113+ if ( methodInfo . DeclaringType is null )
114+ {
115+ throw new ArgumentException ( $ "A { nameof ( targetFactory ) } was provided, but { nameof ( methodInfo ) } does not have a Declaring type.") ;
116+ }
117+
118+ var targetExpression = Expression . Convert ( TargetArg , methodInfo . DeclaringType ) ;
119+ var untargetedRequestDelegate = CreateRequestDelegate ( methodInfo , targetExpression ) ;
120+
121+ return httpContext =>
122+ {
123+ return untargetedRequestDelegate ( targetFactory ( httpContext ) , httpContext ) ;
124+ } ;
125+ }
126+
127+ private static Func < object ? , HttpContext , Task > CreateRequestDelegate ( MethodInfo methodInfo , Expression ? targetExpression )
48128 {
49129 // Non void return type
50130
@@ -62,8 +142,6 @@ public static RequestDelegate BuildRequestDelegate(Delegate action)
62142 // return default;
63143 // }
64144
65- var method = action . Method ;
66-
67145 var consumeBodyDirectly = false ;
68146 var consumeBodyAsForm = false ;
69147 Type ? bodyType = null ;
@@ -72,7 +150,7 @@ public static RequestDelegate BuildRequestDelegate(Delegate action)
72150 // This argument represents the deserialized body returned from IHttpRequestReader
73151 // when the method has a FromBody attribute declared
74152
75- var methodParameters = method . GetParameters ( ) ;
153+ var methodParameters = methodInfo . GetParameters ( ) ;
76154 var args = new List < Expression > ( methodParameters . Length ) ;
77155
78156 foreach ( var parameter in methodParameters )
@@ -156,18 +234,17 @@ public static RequestDelegate BuildRequestDelegate(Delegate action)
156234
157235 MethodCallExpression methodCall ;
158236
159- if ( action . Target is null )
237+ if ( targetExpression is null )
160238 {
161- methodCall = Expression . Call ( method , args ) ;
239+ methodCall = Expression . Call ( methodInfo , args ) ;
162240 }
163241 else
164242 {
165- var castedTarget = Expression . Convert ( TargetArg , action . Target . GetType ( ) ) ;
166- methodCall = Expression . Call ( castedTarget , method , args ) ;
243+ methodCall = Expression . Call ( targetExpression , methodInfo , args ) ;
167244 }
168245
169246 // Exact request delegate match
170- if ( method . ReturnType == typeof ( void ) )
247+ if ( methodInfo . ReturnType == typeof ( void ) )
171248 {
172249 var bodyExpressions = new List < Expression >
173250 {
@@ -177,22 +254,22 @@ public static RequestDelegate BuildRequestDelegate(Delegate action)
177254
178255 body = Expression . Block ( bodyExpressions ) ;
179256 }
180- else if ( AwaitableInfo . IsTypeAwaitable ( method . ReturnType , out var info ) )
257+ else if ( AwaitableInfo . IsTypeAwaitable ( methodInfo . ReturnType , out var info ) )
181258 {
182- if ( method . ReturnType == typeof ( Task ) )
259+ if ( methodInfo . ReturnType == typeof ( Task ) )
183260 {
184261 body = methodCall ;
185262 }
186- else if ( method . ReturnType == typeof ( ValueTask ) )
263+ else if ( methodInfo . ReturnType == typeof ( ValueTask ) )
187264 {
188265 body = Expression . Call (
189266 ExecuteValueTaskMethodInfo ,
190267 methodCall ) ;
191268 }
192- else if ( method . ReturnType . IsGenericType &&
193- method . ReturnType . GetGenericTypeDefinition ( ) == typeof ( Task < > ) )
269+ else if ( methodInfo . ReturnType . IsGenericType &&
270+ methodInfo . ReturnType . GetGenericTypeDefinition ( ) == typeof ( Task < > ) )
194271 {
195- var typeArg = method . ReturnType . GetGenericArguments ( ) [ 0 ] ;
272+ var typeArg = methodInfo . ReturnType . GetGenericArguments ( ) [ 0 ] ;
196273
197274 if ( typeof ( IResult ) . IsAssignableFrom ( typeArg ) )
198275 {
@@ -220,10 +297,10 @@ public static RequestDelegate BuildRequestDelegate(Delegate action)
220297 }
221298 }
222299 }
223- else if ( method . ReturnType . IsGenericType &&
224- method . ReturnType . GetGenericTypeDefinition ( ) == typeof ( ValueTask < > ) )
300+ else if ( methodInfo . ReturnType . IsGenericType &&
301+ methodInfo . ReturnType . GetGenericTypeDefinition ( ) == typeof ( ValueTask < > ) )
225302 {
226- var typeArg = method . ReturnType . GetGenericArguments ( ) [ 0 ] ;
303+ var typeArg = methodInfo . ReturnType . GetGenericArguments ( ) [ 0 ] ;
227304
228305 if ( typeof ( IResult ) . IsAssignableFrom ( typeArg ) )
229306 {
@@ -254,18 +331,18 @@ public static RequestDelegate BuildRequestDelegate(Delegate action)
254331 else
255332 {
256333 // TODO: Handle custom awaitables
257- throw new NotSupportedException ( $ "Unsupported return type: { method . ReturnType } ") ;
334+ throw new NotSupportedException ( $ "Unsupported return type: { methodInfo . ReturnType } ") ;
258335 }
259336 }
260- else if ( typeof ( IResult ) . IsAssignableFrom ( method . ReturnType ) )
337+ else if ( typeof ( IResult ) . IsAssignableFrom ( methodInfo . ReturnType ) )
261338 {
262339 body = Expression . Call ( methodCall , ResultWriteResponseAsync , HttpContextParameter ) ;
263340 }
264- else if ( method . ReturnType == typeof ( string ) )
341+ else if ( methodInfo . ReturnType == typeof ( string ) )
265342 {
266343 body = Expression . Call ( StringResultWriteResponseAsync , HttpResponseExpr , methodCall , Expression . Constant ( CancellationToken . None ) ) ;
267344 }
268- else if ( method . ReturnType . IsValueType )
345+ else if ( methodInfo . ReturnType . IsValueType )
269346 {
270347 var box = Expression . TypeAs ( methodCall , typeof ( object ) ) ;
271348 body = Expression . Call ( JsonResultWriteResponseAsync , HttpResponseExpr , box , Expression . Constant ( CancellationToken . None ) ) ;
@@ -357,10 +434,7 @@ public static RequestDelegate BuildRequestDelegate(Delegate action)
357434 requestDelegate = invoker ;
358435 }
359436
360- return httpContext =>
361- {
362- return requestDelegate ( action . Target , httpContext ) ;
363- } ;
437+ return requestDelegate ;
364438 }
365439
366440 private static ILogger GetLogger ( HttpContext httpContext )
0 commit comments