2
2
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3
3
4
4
using System ;
5
- using System . Collections . Generic ;
6
5
using System . Linq ;
7
- using Microsoft . AspNetCore . Http ;
8
6
using Microsoft . AspNetCore . Mvc . Internal ;
9
7
using Microsoft . AspNetCore . Mvc . ModelBinding ;
10
8
using Microsoft . AspNetCore . Routing . Template ;
13
11
namespace Microsoft . AspNetCore . Mvc . ApplicationModels
14
12
{
15
13
/// <summary>
16
- /// A <see cref="IControllerModelConvention"/> that
17
- /// <list type="bullet">
18
- /// <item>infers binding sources for parameters</item>
19
- /// <item><see cref="BindingInfo.BinderModelName"/> for bound properties and parameters.</item>
20
- /// </list>
14
+ /// An <see cref="IActionModelConvention"/> that infers <see cref="BindingInfo.BindingSource"/> for parameters.
21
15
/// </summary>
22
- public class InferParameterBindingInfoConvention : IControllerModelConvention
16
+ /// <remarks>
17
+ /// The goal of this covention is to make intuitive and easy to document <see cref="BindingSource"/> inferences. The rules are:
18
+ /// <list type="number">
19
+ /// <item>A previously specified <see cref="BindingInfo.BindingSource" /> is never overwritten.</item>
20
+ /// <item>A complex type parameter (<see cref="ModelMetadata.IsComplexType"/>) is assigned <see cref="BindingSource.Body"/>.</item>
21
+ /// <item>Parameter with a name that appears as a route value in ANY route template is assigned <see cref="BindingSource.Path"/>.</item>
22
+ /// <item>All other parameters are <see cref="BindingSource.Query"/>.</item>
23
+ /// </list>
24
+ /// </remarks>
25
+ public class InferParameterBindingInfoConvention : IActionModelConvention
23
26
{
24
27
private readonly IModelMetadataProvider _modelMetadataProvider ;
25
28
@@ -29,41 +32,27 @@ public InferParameterBindingInfoConvention(
29
32
_modelMetadataProvider = modelMetadataProvider ?? throw new ArgumentNullException ( nameof ( modelMetadataProvider ) ) ;
30
33
}
31
34
32
- /// <summary>
33
- /// Gets or sets a value that determines if model binding sources are inferred for action parameters on controllers is suppressed.
34
- /// </summary>
35
- public bool SuppressInferBindingSourcesForParameters { get ; set ; }
35
+ internal bool AllowInferringBindingSourceForCollectionTypesAsFromQuery { get ; set ; }
36
36
37
- protected virtual bool ShouldApply ( ControllerModel controller ) => true ;
37
+ protected virtual bool ShouldApply ( ActionModel action ) => true ;
38
38
39
- public void Apply ( ControllerModel controller )
39
+ public void Apply ( ActionModel action )
40
40
{
41
- if ( controller == null )
41
+ if ( action == null )
42
42
{
43
- throw new ArgumentNullException ( nameof ( controller ) ) ;
43
+ throw new ArgumentNullException ( nameof ( action ) ) ;
44
44
}
45
45
46
- if ( ! ShouldApply ( controller ) )
46
+ if ( ! ShouldApply ( action ) )
47
47
{
48
48
return ;
49
49
}
50
50
51
- InferBoundPropertyModelPrefixes ( controller ) ;
52
-
53
- foreach ( var action in controller . Actions )
54
- {
55
- InferParameterBindingSources ( action ) ;
56
- InferParameterModelPrefixes ( action ) ;
57
- }
51
+ InferParameterBindingSources ( action ) ;
58
52
}
59
53
60
54
internal void InferParameterBindingSources ( ActionModel action )
61
55
{
62
- if ( SuppressInferBindingSourcesForParameters )
63
- {
64
- return ;
65
- }
66
-
67
56
for ( var i = 0 ; i < action . Parameters . Count ; i ++ )
68
57
{
69
58
var parameter = action . Parameters [ i ] ;
@@ -95,56 +84,17 @@ internal void InferParameterBindingSources(ActionModel action)
95
84
// Internal for unit testing.
96
85
internal BindingSource InferBindingSourceForParameter ( ParameterModel parameter )
97
86
{
98
- if ( ParameterExistsInAnyRoute ( parameter . Action , parameter . ParameterName ) )
87
+ if ( IsComplexTypeParameter ( parameter ) )
99
88
{
100
- return BindingSource . Path ;
89
+ return BindingSource . Body ;
101
90
}
102
91
103
- var bindingSource = IsComplexTypeParameter ( parameter ) ?
104
- BindingSource . Body :
105
- BindingSource . Query ;
106
-
107
- return bindingSource ;
108
- }
109
-
110
- // For any complex types that are bound from value providers, set the prefix
111
- // to the empty prefix by default. This makes binding much more predictable
112
- // and describable via ApiExplorer
113
- internal void InferBoundPropertyModelPrefixes ( ControllerModel controllerModel )
114
- {
115
- foreach ( var property in controllerModel . ControllerProperties )
92
+ if ( ParameterExistsInAnyRoute ( parameter . Action , parameter . ParameterName ) )
116
93
{
117
- if ( property . BindingInfo != null &&
118
- property . BindingInfo . BinderModelName == null &&
119
- property . BindingInfo . BindingSource != null &&
120
- ! property . BindingInfo . BindingSource . IsGreedy &&
121
- ! IsFormFile ( property . ParameterType ) )
122
- {
123
- var metadata = _modelMetadataProvider . GetMetadataForProperty (
124
- controllerModel . ControllerType ,
125
- property . PropertyInfo . Name ) ;
126
- if ( metadata . IsComplexType && ! metadata . IsCollectionType )
127
- {
128
- property . BindingInfo . BinderModelName = string . Empty ;
129
- }
130
- }
94
+ return BindingSource . Path ;
131
95
}
132
- }
133
96
134
- internal void InferParameterModelPrefixes ( ActionModel action )
135
- {
136
- foreach ( var parameter in action . Parameters )
137
- {
138
- var bindingInfo = parameter . BindingInfo ;
139
- if ( bindingInfo ? . BindingSource != null &&
140
- bindingInfo . BinderModelName == null &&
141
- ! bindingInfo . BindingSource . IsGreedy &&
142
- ! IsFormFile ( parameter . ParameterType ) &&
143
- IsComplexTypeParameter ( parameter ) )
144
- {
145
- parameter . BindingInfo . BinderModelName = string . Empty ;
146
- }
147
- }
97
+ return BindingSource . Query ;
148
98
}
149
99
150
100
private bool ParameterExistsInAnyRoute ( ActionModel action , string parameterName )
@@ -171,13 +121,13 @@ private bool IsComplexTypeParameter(ParameterModel parameter)
171
121
// No need for information from attributes on the parameter. Just use its type.
172
122
var metadata = _modelMetadataProvider
173
123
. GetMetadataForType ( parameter . ParameterInfo . ParameterType ) ;
174
- return metadata . IsComplexType && ! metadata . IsCollectionType ;
175
- }
176
124
177
- private static bool IsFormFile ( Type parameterType )
178
- {
179
- return typeof ( IFormFile ) . IsAssignableFrom ( parameterType ) ||
180
- typeof ( IEnumerable < IFormFile > ) . IsAssignableFrom ( parameterType ) ;
125
+ if ( AllowInferringBindingSourceForCollectionTypesAsFromQuery && metadata . IsCollectionType )
126
+ {
127
+ return false ;
128
+ }
129
+
130
+ return metadata . IsComplexType ;
181
131
}
182
132
}
183
133
}
0 commit comments