forked from aspnet/AspNetWebStack
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathDefaultActionValueBinder.cs
More file actions
120 lines (103 loc) · 5.18 KB
/
Copy pathDefaultActionValueBinder.cs
File metadata and controls
120 lines (103 loc) · 5.18 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading;
using System.Web.Http.Controllers;
using System.Web.Http.Internal;
using System.Web.Http.Properties;
namespace System.Web.Http.ModelBinding
{
public class DefaultActionValueBinder : IActionValueBinder
{
/// <summary>
/// Implementation of <see cref="IActionValueBinder"/>, Primary entry point for binding parameters for an action.
/// </summary>
public virtual HttpActionBinding GetBinding(HttpActionDescriptor actionDescriptor)
{
if (actionDescriptor == null)
{
throw Error.ArgumentNull("actionDescriptor");
}
HttpParameterDescriptor[] parameters = actionDescriptor.GetParameters().ToArray();
HttpParameterBinding[] binders = Array.ConvertAll(parameters, GetParameterBinding);
HttpActionBinding actionBinding = new HttpActionBinding(actionDescriptor, binders);
EnsureOneBodyParameter(actionBinding);
return actionBinding;
}
/// <summary>
/// Update actionBinding to enforce there is at most 1 body parameter.
/// If there are multiple, convert them all to <see cref="ErrorParameterBinding"/>
/// </summary>
private static void EnsureOneBodyParameter(HttpActionBinding actionBinding)
{
IList<HttpParameterDescriptor> parameters = actionBinding.ActionDescriptor.GetParameters();
int idxFromBody = -1;
for (int i = 0; i < actionBinding.ParameterBindings.Length; i++)
{
if (actionBinding.ParameterBindings[i].WillReadBody)
{
if (idxFromBody >= 0)
{
// This is the 2nd parameter to read from the body. Flag an error.
string name1 = parameters[idxFromBody].ParameterName;
string name2 = parameters[i].ParameterName;
string message = Error.Format(SRResources.ParameterBindingCantHaveMultipleBodyParameters, name1, name2);
actionBinding.ParameterBindings[i] = new ErrorParameterBinding(parameters[i], message);
actionBinding.ParameterBindings[idxFromBody] = new ErrorParameterBinding(parameters[idxFromBody], message);
}
else
{
idxFromBody = i;
}
}
}
}
// Determine how a single parameter will get bound.
// This is all sync. We don't need to actually read the body just to determine that we'll bind to the body.
protected virtual HttpParameterBinding GetParameterBinding(HttpParameterDescriptor parameter)
{
// Attribute has the highest precedence
// Presence of a model binder attribute overrides.
ParameterBindingAttribute attr = parameter.ParameterBinderAttribute;
if (attr != null)
{
return attr.GetBinding(parameter);
}
// No attribute, so lookup in global map.
ParameterBindingRulesCollection pb = parameter.Configuration.ParameterBindingRules;
if (pb != null)
{
HttpParameterBinding binding = pb.LookupBinding(parameter);
if (binding != null)
{
return binding;
}
}
// Not explicitly specified in global map or attribute.
// Use a default policy to determine it. These are catch-all policies.
Type type = parameter.ParameterType;
if (TypeHelper.CanConvertFromString(type))
{
// For simple types, the default is to look in URI. Exactly as if the parameter had a [FromUri] attribute.
return parameter.BindWithAttribute(new FromUriAttribute());
}
// Fallback. Must be a complex type. Default is to look in body. Exactly as if this type had a [FromBody] attribute.
attr = new FromBodyAttribute();
return attr.GetBinding(parameter);
}
// Create an instance and add some default binders
internal static ParameterBindingRulesCollection GetDefaultParameterBinders()
{
ParameterBindingRulesCollection pb = new ParameterBindingRulesCollection();
pb.Add(typeof(CancellationToken), parameter => new CancellationTokenParameterBinding(parameter));
pb.Add(typeof(HttpRequestMessage), parameter => new HttpRequestParameterBinding(parameter));
// Warning binder for HttpContent.
pb.Add(parameter => typeof(HttpContent).IsAssignableFrom(parameter.ParameterType) ?
parameter.BindAsError(Error.Format(SRResources.ParameterBindingIllegalType, parameter.ParameterType.Name, parameter.ParameterName))
: null);
return pb;
}
}
}