@@ -60,9 +60,11 @@ public static partial class RequestDelegateFactory
6060 /// Creates a <see cref="RequestDelegate"/> implementation for <paramref name="action"/>.
6161 /// </summary>
6262 /// <param name="action">A request handler with any number of custom parameters that often produces a response with its return value.</param>
63- /// <param name="serviceProvider ">The <see cref="IServiceProvider "/> instance used to detect which parameters are services .</param>
63+ /// <param name="options ">The <see cref="RequestDelegateFactoryOptions "/> used to configure the behavior of the handler .</param>
6464 /// <returns>The <see cref="RequestDelegate"/>.</returns>
65- public static RequestDelegate Create ( Delegate action , IServiceProvider ? serviceProvider )
65+ #pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters
66+ public static RequestDelegate Create ( Delegate action , RequestDelegateFactoryOptions ? options = null )
67+ #pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters
6668 {
6769 if ( action is null )
6870 {
@@ -75,69 +77,60 @@ public static RequestDelegate Create(Delegate action, IServiceProvider? serviceP
7577 null => null ,
7678 } ;
7779
78- var targetableRequestDelegate = CreateTargetableRequestDelegate ( action . Method , serviceProvider , targetExpression ) ;
80+ var targetableRequestDelegate = CreateTargetableRequestDelegate ( action . Method , options , targetExpression ) ;
7981
8082 return httpContext =>
8183 {
8284 return targetableRequestDelegate ( action . Target , httpContext ) ;
8385 } ;
8486 }
8587
86- /// <summary>
87- /// Creates a <see cref="RequestDelegate"/> implementation for <paramref name="methodInfo"/>.
88- /// </summary>
89- /// <param name="methodInfo">A static request handler with any number of custom parameters that often produces a response with its return value.</param>
90- /// <param name="serviceProvider">The <see cref="IServiceProvider"/> instance used to detect which parameters are services.</param>
91- /// <returns>The <see cref="RequestDelegate"/>.</returns>
92- public static RequestDelegate Create ( MethodInfo methodInfo , IServiceProvider ? serviceProvider )
93- {
94- if ( methodInfo is null )
95- {
96- throw new ArgumentNullException ( nameof ( methodInfo ) ) ;
97- }
98-
99- var targetableRequestDelegate = CreateTargetableRequestDelegate ( methodInfo , serviceProvider , targetExpression : null ) ;
100-
101- return httpContext =>
102- {
103- return targetableRequestDelegate ( null , httpContext ) ;
104- } ;
105- }
106-
10788 /// <summary>
10889 /// Creates a <see cref="RequestDelegate"/> implementation for <paramref name="methodInfo"/>.
10990 /// </summary>
11091 /// <param name="methodInfo">A request handler with any number of custom parameters that often produces a response with its return value.</param>
111- /// <param name="serviceProvider">The <see cref="IServiceProvider"/> instance used to detect which parameters are services.</param>
11292 /// <param name="targetFactory">Creates the <see langword="this"/> for the non-static method.</param>
93+ /// <param name="options">The <see cref="RequestDelegateFactoryOptions"/> used to configure the behavior of the handler.</param>
11394 /// <returns>The <see cref="RequestDelegate"/>.</returns>
114- public static RequestDelegate Create ( MethodInfo methodInfo , IServiceProvider ? serviceProvider , Func < HttpContext , object > targetFactory )
95+ #pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters
96+ public static RequestDelegate Create ( MethodInfo methodInfo , Func < HttpContext , object > ? targetFactory = null , RequestDelegateFactoryOptions ? options = null )
97+ #pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters
11598 {
11699 if ( methodInfo is null )
117100 {
118101 throw new ArgumentNullException ( nameof ( methodInfo ) ) ;
119102 }
120103
121- if ( targetFactory is null )
104+ if ( methodInfo . DeclaringType is null )
122105 {
123- throw new ArgumentNullException ( nameof ( targetFactory ) ) ;
106+ throw new ArgumentException ( $ " { nameof ( methodInfo ) } does not have a declaring type." ) ;
124107 }
125108
126- if ( methodInfo . DeclaringType is null )
109+ if ( targetFactory is null )
127110 {
128- throw new ArgumentException ( $ "A { nameof ( targetFactory ) } was provided, but { nameof ( methodInfo ) } does not have a Declaring type.") ;
111+ if ( methodInfo . IsStatic )
112+ {
113+ var untargetableRequestDelegate = CreateTargetableRequestDelegate ( methodInfo , options , targetExpression : null ) ;
114+
115+ return httpContext =>
116+ {
117+ return untargetableRequestDelegate ( null , httpContext ) ;
118+ } ;
119+ }
120+
121+ targetFactory = context => Activator . CreateInstance ( methodInfo . DeclaringType ) ! ;
129122 }
130123
131124 var targetExpression = Expression . Convert ( TargetExpr , methodInfo . DeclaringType ) ;
132- var targetableRequestDelegate = CreateTargetableRequestDelegate ( methodInfo , serviceProvider , targetExpression ) ;
125+ var targetableRequestDelegate = CreateTargetableRequestDelegate ( methodInfo , options , targetExpression ) ;
133126
134127 return httpContext =>
135128 {
136129 return targetableRequestDelegate ( targetFactory ( httpContext ) , httpContext ) ;
137130 } ;
138131 }
139132
140- private static Func < object ? , HttpContext , Task > CreateTargetableRequestDelegate ( MethodInfo methodInfo , IServiceProvider ? serviceProvider , Expression ? targetExpression )
133+ private static Func < object ? , HttpContext , Task > CreateTargetableRequestDelegate ( MethodInfo methodInfo , RequestDelegateFactoryOptions ? options , Expression ? targetExpression )
141134 {
142135 // Non void return type
143136
@@ -157,9 +150,14 @@ public static RequestDelegate Create(MethodInfo methodInfo, IServiceProvider? se
157150
158151 var factoryContext = new FactoryContext ( )
159152 {
160- ServiceProviderIsService = serviceProvider ? . GetService < IServiceProviderIsService > ( )
153+ ServiceProviderIsService = options ? . ServiceProvider ? . GetService < IServiceProviderIsService > ( )
161154 } ;
162155
156+ if ( options ? . RouteParameterNames is { } routeParameterNames )
157+ {
158+ factoryContext . RouteParameters = new ( routeParameterNames ) ;
159+ }
160+
163161 var arguments = CreateArguments ( methodInfo . GetParameters ( ) , factoryContext ) ;
164162
165163 var responseWritingMethodCall = factoryContext . TryParseParams . Count > 0 ?
@@ -202,6 +200,11 @@ private static Expression CreateArgument(ParameterInfo parameter, FactoryContext
202200
203201 if ( parameterCustomAttributes . OfType < IFromRouteMetadata > ( ) . FirstOrDefault ( ) is { } routeAttribute )
204202 {
203+ if ( factoryContext . RouteParameters is { } routeParams && ! routeParams . Contains ( parameter . Name ) )
204+ {
205+ throw new InvalidOperationException ( $ "{ parameter . Name } is not a route paramter.") ;
206+ }
207+
205208 return BindParameterFromProperty ( parameter , RouteValuesExpr , routeAttribute . Name ?? parameter . Name , factoryContext ) ;
206209 }
207210 else if ( parameterCustomAttributes . OfType < IFromQueryMetadata > ( ) . FirstOrDefault ( ) is { } queryAttribute )
@@ -242,6 +245,13 @@ private static Expression CreateArgument(ParameterInfo parameter, FactoryContext
242245 }
243246 else if ( parameter . ParameterType == typeof ( string ) || TryParseMethodCache . HasTryParseMethod ( parameter ) )
244247 {
248+ // We're in the fallback case and we have a parameter and route parameter match so don't fallback
249+ // to query string in this case
250+ if ( factoryContext . RouteParameters is { } routeParams && routeParams . Contains ( parameter . Name ) )
251+ {
252+ return BindParameterFromProperty ( parameter , RouteValuesExpr , parameter . Name , factoryContext ) ;
253+ }
254+
245255 return BindParameterFromRouteValueOrQueryString ( parameter , parameter . Name , factoryContext ) ;
246256 }
247257 else
@@ -814,6 +824,7 @@ private class FactoryContext
814824 public Type ? JsonRequestBodyType { get ; set ; }
815825 public bool AllowEmptyRequestBody { get ; set ; }
816826 public IServiceProviderIsService ? ServiceProviderIsService { get ; init ; }
827+ public List < string > ? RouteParameters { get ; set ; }
817828
818829 public bool UsingTempSourceString { get ; set ; }
819830 public List < ( ParameterExpression , Expression ) > TryParseParams { get ; } = new ( ) ;
0 commit comments