@@ -39,7 +39,6 @@ namespace Microsoft.AspNetCore.Http;
39
39
public static partial class RequestDelegateFactory
40
40
{
41
41
private static readonly ParameterBindingMethodCache ParameterBindingMethodCache = new ( ) ;
42
- private static readonly FormDataMapperOptions FormDataMapperOptions = new ( ) ;
43
42
44
43
private static readonly MethodInfo ExecuteTaskWithEmptyResultMethod = typeof ( RequestDelegateFactory ) . GetMethod ( nameof ( ExecuteTaskWithEmptyResult ) , BindingFlags . NonPublic | BindingFlags . Static ) ! ;
45
44
private static readonly MethodInfo ExecuteValueTaskWithEmptyResultMethod = typeof ( RequestDelegateFactory ) . GetMethod ( nameof ( ExecuteValueTaskWithEmptyResult ) , BindingFlags . NonPublic | BindingFlags . Static ) ! ;
@@ -333,6 +332,11 @@ private static IReadOnlyList<object> AsReadOnlyList(IList<object> metadata)
333
332
// inference is skipped internally if necessary.
334
333
factoryContext . ArgumentExpressions ??= CreateArgumentsAndInferMetadata ( methodInfo , factoryContext ) ;
335
334
335
+ // Although we can re-use the cached argument expressions for most cases, parameters that are bound
336
+ // using the new form mapping logic are a special exception because we need to account for the `FormOptionsMetadata`
337
+ // added to the builder _during_ the construction of the parameter binding.
338
+ UpdateFormBindingArgumentExpressions ( factoryContext ) ;
339
+
336
340
factoryContext . MethodCall = CreateMethodCall ( methodInfo , targetExpression , factoryContext . ArgumentExpressions ) ;
337
341
EndpointFilterDelegate ? filterPipeline = null ;
338
342
var returnType = methodInfo . ReturnType ;
@@ -753,7 +757,7 @@ private static Expression CreateArgument(ParameterInfo parameter, RequestDelegat
753
757
( parameter . ParameterType . IsArray && ParameterBindingMethodCache . HasTryParseMethod ( parameter . ParameterType . GetElementType ( ) ! ) ) ;
754
758
return useSimpleBinding
755
759
? BindParameterFromFormItem ( parameter , formAttribute . Name ?? parameter . Name , factoryContext )
756
- : BindComplexParameterFromFormItem ( parameter , formAttribute . Name ?? parameter . Name , factoryContext ) ;
760
+ : BindComplexParameterFromFormItem ( parameter , string . IsNullOrEmpty ( formAttribute . Name ) ? parameter . Name : formAttribute . Name , factoryContext ) ;
757
761
}
758
762
else if ( parameter . CustomAttributes . Any ( a => typeof ( IFromServiceMetadata ) . IsAssignableFrom ( a . AttributeType ) ) )
759
763
{
@@ -1982,14 +1986,40 @@ private static Expression BindParameterFromFormItem(
1982
1986
"form" ) ;
1983
1987
}
1984
1988
1989
+ private static void UpdateFormBindingArgumentExpressions ( RequestDelegateFactoryContext factoryContext )
1990
+ {
1991
+ if ( factoryContext . ArgumentExpressions == null || factoryContext . ArgumentExpressions . Length == 0 )
1992
+ {
1993
+ return ;
1994
+ }
1995
+
1996
+ for ( var i = 0 ; i < factoryContext . ArgumentExpressions . Length ; i ++ )
1997
+ {
1998
+ var parameter = factoryContext . Parameters [ i ] ;
1999
+ var key = parameter . Name ! ;
2000
+ if ( factoryContext . TrackedParameters . TryGetValue ( key , out var trackedParameter ) && trackedParameter == RequestDelegateFactoryConstants . FormBindingAttribute )
2001
+ {
2002
+ factoryContext . ArgumentExpressions [ i ] = BindComplexParameterFromFormItem ( parameter , key , factoryContext ) ;
2003
+ }
2004
+ }
2005
+ }
2006
+
1985
2007
private static Expression BindComplexParameterFromFormItem (
1986
2008
ParameterInfo parameter ,
1987
2009
string key ,
1988
2010
RequestDelegateFactoryContext factoryContext )
1989
2011
{
1990
2012
factoryContext . FirstFormRequestBodyParameter ??= parameter ;
1991
- factoryContext . TrackedParameters . Add ( key , RequestDelegateFactoryConstants . FormAttribute ) ;
2013
+ factoryContext . TrackedParameters . TryAdd ( key , RequestDelegateFactoryConstants . FormBindingAttribute ) ;
1992
2014
factoryContext . ReadForm = true ;
2015
+ var formDataMapperOptions = new FormDataMapperOptions ( ) ;
2016
+ var formMappingOptionsMetadatas = factoryContext . EndpointBuilder . Metadata . OfType < FormMappingOptionsMetadata > ( ) ;
2017
+ foreach ( var formMappingOptionsMetadata in formMappingOptionsMetadatas )
2018
+ {
2019
+ formDataMapperOptions . MaxRecursionDepth = formMappingOptionsMetadata . MaxRecursionDepth ?? formDataMapperOptions . MaxRecursionDepth ;
2020
+ formDataMapperOptions . MaxCollectionSize = formMappingOptionsMetadata . MaxCollectionSize ?? formDataMapperOptions . MaxCollectionSize ;
2021
+ formDataMapperOptions . MaxKeyBufferSize = formMappingOptionsMetadata . MaxKeySize ?? formDataMapperOptions . MaxKeyBufferSize ;
2022
+ }
1993
2023
1994
2024
// var name_local;
1995
2025
// var name_reader;
@@ -2001,19 +2031,19 @@ private static Expression BindComplexParameterFromFormItem(
2001
2031
var formBuffer = Expression . Variable ( typeof ( char [ ] ) , "form_buffer" ) ;
2002
2032
2003
2033
// ProcessForm(context.Request.Form, form_dict, form_buffer);
2004
- var processFormExpr = Expression . Call ( ProcessFormMethod , FormExpr , formDict , formBuffer ) ;
2034
+ var processFormExpr = Expression . Call ( ProcessFormMethod , FormExpr , Expression . Constant ( formDataMapperOptions . MaxKeyBufferSize ) , formDict , formBuffer ) ;
2005
2035
// name_reader = new FormDataReader(form_dict, CultureInfo.InvariantCulture, form_buffer.AsMemory(0, FormDataMapperOptions.MaxKeyBufferSize));
2006
2036
var initializeReaderExpr = Expression . Assign (
2007
2037
formReader ,
2008
2038
Expression . New ( FormDataReaderConstructor ,
2009
2039
formDict ,
2010
2040
Expression . Constant ( CultureInfo . InvariantCulture ) ,
2011
- Expression . Call ( AsMemoryMethod , formBuffer , Expression . Constant ( 0 ) , Expression . Constant ( FormDataMapperOptions . MaxKeyBufferSize ) ) ) ) ;
2041
+ Expression . Call ( AsMemoryMethod , formBuffer , Expression . Constant ( 0 ) , Expression . Constant ( formDataMapperOptions . MaxKeyBufferSize ) ) ) ) ;
2012
2042
// FormDataMapper.Map<string>(name_reader, FormDataMapperOptions);
2013
2043
var invokeMapMethodExpr = Expression . Call (
2014
2044
FormDataMapperMapMethod . MakeGenericMethod ( parameter . ParameterType ) ,
2015
2045
formReader ,
2016
- Expression . Constant ( FormDataMapperOptions ) ) ;
2046
+ Expression . Constant ( formDataMapperOptions ) ) ;
2017
2047
// if (form_buffer != null)
2018
2048
// {
2019
2049
// ArrayPool<char>.Shared.Return(form_buffer, false);
@@ -2039,15 +2069,15 @@ private static Expression BindComplexParameterFromFormItem(
2039
2069
) ;
2040
2070
}
2041
2071
2042
- private static void ProcessForm ( IFormCollection form , ref IReadOnlyDictionary < FormKey , StringValues > formDictionary , ref char [ ] buffer )
2072
+ private static void ProcessForm ( IFormCollection form , int maxKeyBufferSize , ref IReadOnlyDictionary < FormKey , StringValues > formDictionary , ref char [ ] buffer )
2043
2073
{
2044
2074
var dictionary = new Dictionary < FormKey , StringValues > ( ) ;
2045
2075
foreach ( var ( key , value ) in form )
2046
2076
{
2047
2077
dictionary . Add ( new FormKey ( key . AsMemory ( ) ) , value ) ;
2048
2078
}
2049
2079
formDictionary = dictionary . AsReadOnly ( ) ;
2050
- buffer = ArrayPool < char > . Shared . Rent ( FormDataMapperOptions . MaxKeyBufferSize ) ;
2080
+ buffer = ArrayPool < char > . Shared . Rent ( maxKeyBufferSize ) ;
2051
2081
}
2052
2082
2053
2083
private static Expression BindParameterFromFormFiles (
@@ -2447,6 +2477,7 @@ private static class RequestDelegateFactoryConstants
2447
2477
public const string ServiceAttribute = "Service (Attribute)" ;
2448
2478
public const string FormFileAttribute = "Form File (Attribute)" ;
2449
2479
public const string FormAttribute = "Form (Attribute)" ;
2480
+ public const string FormBindingAttribute = "Form Binding (Attribute)" ;
2450
2481
public const string RouteParameter = "Route (Inferred)" ;
2451
2482
public const string QueryStringParameter = "Query String (Inferred)" ;
2452
2483
public const string ServiceParameter = "Services (Inferred)" ;
0 commit comments