11
11
using System . Reflection ;
12
12
using System . Threading ;
13
13
using System . Threading . Tasks ;
14
- using Microsoft . AspNetCore . Http ;
15
14
using Microsoft . AspNetCore . Http . Metadata ;
16
15
using Microsoft . Extensions . DependencyInjection ;
17
16
using Microsoft . Extensions . Internal ;
18
17
using Microsoft . Extensions . Logging ;
19
18
20
- namespace Microsoft . AspNetCore . Routing . Internal
19
+ namespace Microsoft . AspNetCore . Http
21
20
{
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
23
25
{
24
26
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 ) ! ;
32
34
private static readonly MethodInfo GetRequiredServiceMethodInfo = typeof ( ServiceProviderServiceExtensions ) . GetMethod ( nameof ( ServiceProviderServiceExtensions . GetRequiredService ) , BindingFlags . Public | BindingFlags . Static , new Type [ ] { typeof ( IServiceProvider ) } ) ! ;
33
35
private static readonly MethodInfo ResultWriteResponseAsync = typeof ( IResult ) . GetMethod ( nameof ( IResult . ExecuteAsync ) , BindingFlags . Public | BindingFlags . Instance ) ! ;
34
36
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
44
46
private static readonly MemberExpression HttpResponseExpr = Expression . Property ( HttpContextParameter , nameof ( HttpContext . Response ) ) ;
45
47
private static readonly MemberExpression RequestAbortedExpr = Expression . Property ( HttpContextParameter , nameof ( HttpContext . RequestAborted ) ) ;
46
48
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 )
48
128
{
49
129
// Non void return type
50
130
@@ -62,8 +142,6 @@ public static RequestDelegate BuildRequestDelegate(Delegate action)
62
142
// return default;
63
143
// }
64
144
65
- var method = action . Method ;
66
-
67
145
var consumeBodyDirectly = false ;
68
146
var consumeBodyAsForm = false ;
69
147
Type ? bodyType = null ;
@@ -72,7 +150,7 @@ public static RequestDelegate BuildRequestDelegate(Delegate action)
72
150
// This argument represents the deserialized body returned from IHttpRequestReader
73
151
// when the method has a FromBody attribute declared
74
152
75
- var methodParameters = method . GetParameters ( ) ;
153
+ var methodParameters = methodInfo . GetParameters ( ) ;
76
154
var args = new List < Expression > ( methodParameters . Length ) ;
77
155
78
156
foreach ( var parameter in methodParameters )
@@ -156,18 +234,17 @@ public static RequestDelegate BuildRequestDelegate(Delegate action)
156
234
157
235
MethodCallExpression methodCall ;
158
236
159
- if ( action . Target is null )
237
+ if ( targetExpression is null )
160
238
{
161
- methodCall = Expression . Call ( method , args ) ;
239
+ methodCall = Expression . Call ( methodInfo , args ) ;
162
240
}
163
241
else
164
242
{
165
- var castedTarget = Expression . Convert ( TargetArg , action . Target . GetType ( ) ) ;
166
- methodCall = Expression . Call ( castedTarget , method , args ) ;
243
+ methodCall = Expression . Call ( targetExpression , methodInfo , args ) ;
167
244
}
168
245
169
246
// Exact request delegate match
170
- if ( method . ReturnType == typeof ( void ) )
247
+ if ( methodInfo . ReturnType == typeof ( void ) )
171
248
{
172
249
var bodyExpressions = new List < Expression >
173
250
{
@@ -177,22 +254,22 @@ public static RequestDelegate BuildRequestDelegate(Delegate action)
177
254
178
255
body = Expression . Block ( bodyExpressions ) ;
179
256
}
180
- else if ( AwaitableInfo . IsTypeAwaitable ( method . ReturnType , out var info ) )
257
+ else if ( AwaitableInfo . IsTypeAwaitable ( methodInfo . ReturnType , out var info ) )
181
258
{
182
- if ( method . ReturnType == typeof ( Task ) )
259
+ if ( methodInfo . ReturnType == typeof ( Task ) )
183
260
{
184
261
body = methodCall ;
185
262
}
186
- else if ( method . ReturnType == typeof ( ValueTask ) )
263
+ else if ( methodInfo . ReturnType == typeof ( ValueTask ) )
187
264
{
188
265
body = Expression . Call (
189
266
ExecuteValueTaskMethodInfo ,
190
267
methodCall ) ;
191
268
}
192
- else if ( method . ReturnType . IsGenericType &&
193
- method . ReturnType . GetGenericTypeDefinition ( ) == typeof ( Task < > ) )
269
+ else if ( methodInfo . ReturnType . IsGenericType &&
270
+ methodInfo . ReturnType . GetGenericTypeDefinition ( ) == typeof ( Task < > ) )
194
271
{
195
- var typeArg = method . ReturnType . GetGenericArguments ( ) [ 0 ] ;
272
+ var typeArg = methodInfo . ReturnType . GetGenericArguments ( ) [ 0 ] ;
196
273
197
274
if ( typeof ( IResult ) . IsAssignableFrom ( typeArg ) )
198
275
{
@@ -220,10 +297,10 @@ public static RequestDelegate BuildRequestDelegate(Delegate action)
220
297
}
221
298
}
222
299
}
223
- else if ( method . ReturnType . IsGenericType &&
224
- method . ReturnType . GetGenericTypeDefinition ( ) == typeof ( ValueTask < > ) )
300
+ else if ( methodInfo . ReturnType . IsGenericType &&
301
+ methodInfo . ReturnType . GetGenericTypeDefinition ( ) == typeof ( ValueTask < > ) )
225
302
{
226
- var typeArg = method . ReturnType . GetGenericArguments ( ) [ 0 ] ;
303
+ var typeArg = methodInfo . ReturnType . GetGenericArguments ( ) [ 0 ] ;
227
304
228
305
if ( typeof ( IResult ) . IsAssignableFrom ( typeArg ) )
229
306
{
@@ -254,18 +331,18 @@ public static RequestDelegate BuildRequestDelegate(Delegate action)
254
331
else
255
332
{
256
333
// TODO: Handle custom awaitables
257
- throw new NotSupportedException ( $ "Unsupported return type: { method . ReturnType } ") ;
334
+ throw new NotSupportedException ( $ "Unsupported return type: { methodInfo . ReturnType } ") ;
258
335
}
259
336
}
260
- else if ( typeof ( IResult ) . IsAssignableFrom ( method . ReturnType ) )
337
+ else if ( typeof ( IResult ) . IsAssignableFrom ( methodInfo . ReturnType ) )
261
338
{
262
339
body = Expression . Call ( methodCall , ResultWriteResponseAsync , HttpContextParameter ) ;
263
340
}
264
- else if ( method . ReturnType == typeof ( string ) )
341
+ else if ( methodInfo . ReturnType == typeof ( string ) )
265
342
{
266
343
body = Expression . Call ( StringResultWriteResponseAsync , HttpResponseExpr , methodCall , Expression . Constant ( CancellationToken . None ) ) ;
267
344
}
268
- else if ( method . ReturnType . IsValueType )
345
+ else if ( methodInfo . ReturnType . IsValueType )
269
346
{
270
347
var box = Expression . TypeAs ( methodCall , typeof ( object ) ) ;
271
348
body = Expression . Call ( JsonResultWriteResponseAsync , HttpResponseExpr , box , Expression . Constant ( CancellationToken . None ) ) ;
@@ -357,10 +434,7 @@ public static RequestDelegate BuildRequestDelegate(Delegate action)
357
434
requestDelegate = invoker ;
358
435
}
359
436
360
- return httpContext =>
361
- {
362
- return requestDelegate ( action . Target , httpContext ) ;
363
- } ;
437
+ return requestDelegate ;
364
438
}
365
439
366
440
private static ILogger GetLogger ( HttpContext httpContext )
0 commit comments