Matching Action Method Parameters
In this lesson, we will learn how to pass parameters to action methods, taking them from various parts of the current request and from application internal sources.
We'll cover the following
Action method parameters can be filled with information from various sources. In previous lessons, we have seen three of them: route parameters, query-string, and form fields.
By default, parameters that are simple types are taken from the route parameters and query-string, while complex types with several properties and collections are bound with data extracted from the request body. By default, no other sources are considered, but we can force the usage of specific sources with parameters’ attributes.
Value lookup and parameters’ attributes
We can force the source to be used for filling a parameter with an attribute that precedes a parameter.
The table below describes all options:
Attribute | Example | Source |
---|---|---|
FromQueryAttribute | …, [FromQuery] string x, … | The value is taken from a query string parameter. |
FromRouteAttribute | …, [FromRoute] string id, … | The value is taken from a route parameter. |
FromHeaderAttribute | …, [FromHeader(Name=“Accept-Language”)] string id, … | The value is taken from a request header. |
FromFormAttribute | …, [FromForm] MyModelType model, … | The value is taken from form parameters. |
FromBodyAttribute | …, [FromBody] MyModelType model, … | The value is taken from the request body with an algorithm that depends on the request Content-Type. |
FromServicesAttribute | …, [FromServices] MyServiceType model, … | The value is taken from an internal source, called dependency injection container. |
Dependency injection will be described in a dedicated chapter.
In the case of request headers, route parameters, query string parameters, and form fields a case-insensitive match is performed between the source name and the parameter name. This is used to locate the right source for filling each parameter. Then a conversion is performed between the string contained in the source and the type of the target parameter.
If the parameter is a simple type, its name must match the name of the source. When, instead, the parameter is a complex object which has several properties that might possibly contain nested complex objects, a match is attempted for each leaf of the object tree.
Thus, suppose that the name of the parameter is model
, and that the binding algorithm is looking for a property called NestedProperty1
, of a nested object contained in a root property called Property1
, then the algorithm looks first, for a source named "model.NestedProperty1.Property1"
, and then, if none is found, for a source named "NestedProperty1.Property1"
.
In a few words, we try names that correspond to the paths in the object trees where branch names are concatenated separated by a period (.
). First, the algorithm tries a path that contains the parameter name, then the path without the parameter name. All matches are case insensitive. This algorithm has customization points and is also able to process arrays and collections. We will analyze both customization and collection processing in the next lesson that is dedicated to Binders.
The name of the action method parameter can be overridden by passing a lookup name to the Name
property attribute that decorates the parameter, as shown in the example for the FromHeaderAttribute
in the previous table. In which case this lookup name replaces the name of the parameter in the whole matching process. This is particularly useful when the name to match is not a legal name for a C# method parameter, as it is the case for several HTTP request headers.
When the body doesn’t contains a Content-Type
other than form data, and a parameter must take its content from the body. This is because it has either been decorated with a [FromBody]
attribute, or the parameter is not decorated with any attribute and it is a complex type. In that case, no name match is performed as in the other cases, but a processor that is specific for the body Content-Type
is invoked to process the content and fill the parameter with the result of the processing. These processors are called Formatters and will be discussed in a dedicated lesson.
The example below shows complex bindings that involve several sources:
Get hands-on with 1400+ tech skills courses.