Open yohny opened 3 years ago
@captainsafia I wouldn't classify this as a bug. We document this behavior (maybe the docs could do with some helping) - https://docs.microsoft.com/en-us/aspnet/core/mvc/models/model-binding?view=aspnetcore-5.0#prefix--parameter-name and have an analyzer that tries and complains about it for non-api controllers - https://github.com/dotnet/aspnetcore/blob/main/src/Mvc/Mvc.Analyzers/src/TopLevelParameterNameAnalyzer.cs.
@pranavkm Thanks for clarifying! We annotated this as a bug during triage because it seemed like buggy behavior.
So, it is intentional that if your parameter name is the same as one of the property names, the lookup will fail? What's responsible for this behavior?
So, it is intentional that if your parameter name is the same as one of the property names, the lookup will fail? What's responsible for this behavior?
For a model record Person(string Name, int Age)
, Mvc supports binding both person.Name
/ person.Age
or Name
/ Age
. However it only falls back to the prefix-less lookups if it cannot find any values that begin with the person
prefix. Note that person
is also a valid value for this thing since you could have a type converter that converts person=Test|10
and produces a Person
instance out of it.
The decision tree looks like so:
person
prefix? If so, assume all values have a person.
prefixThe special case is if you have a parameter and a property with the same name: public IActionResult Post(Person name)
with ?Name=SomeName&Age=10
. In this case, binding decides to use the prefix and starts looking for name.Name
and name.Age
.
Usually changing model binding's behavior is tricky because user apps, custom model binders, or value providers may have built solutions around the current behavior and we have very limited ways to know outside of users telling us when we break them.
I agree that this can be tricky to change, so if not possible than at least it should be documented better. The current documentation does not describe the behavior as you put it in your comment and does not mention any potential issues/rules for parameter name. Also the mentioned analyzer could be part of default ASP.NET Core Web API projects.
The analyzer is on by default, but it skips ApiController
instances https://github.com/dotnet/aspnetcore/blob/main/src/Mvc/Mvc.Analyzers/src/TopLevelParameterNameAnalyzer.cs#L57-L62. We could update the code here and have it apply to complex parameters with FromQuery
/ FromForm
/ FromRoute
etc attributes.
Thanks for contacting us.
We're moving this issue to the .NET 8 Planning
milestone for future evaluation / consideration. We would like to keep this around to collect more feedback, which can help us with prioritizing this work. We will re-evaluate this issue, during our next planning meeting(s).
If we later determine, that the issue has no community involvement, or it's very rare and low-impact issue, we will close it - so that the team can focus on more important and high impact issues.
To learn more about what to expect next and how this issue will be handled you can read more about our triage process here.
Removing the doc label since the behavior is documented here.
Adding the analyzer label since we want to apply the analyzer updates that were mentioned above.
I have a ASP.NET core Web API project (fresh ASP.NET Core Web API template from Visual Studio 2019, no customizations), with a controller action like this:
the action parameter
dto
is a complex type bound from query and it looks like this:If I call the API using URL
<host>/<controller>/search?term=abc&caseInsensitive=true
it all works as expected -Term
property contains"abc"
andCaseInsensitive
istrue
.This makes sense, as according to https://docs.microsoft.com/en-us/aspnet/core/mvc/models/model-binding?view=aspnetcore-5.0#prefix--parameter-name, the binding first tries to find values prefixed with parameter name (aka. it searches for keys
"dto.Term"
and"dto.CaseInsensitive"
in URL query string) and if that fails it tries the properties without prefix (aka. it searches for"Term"
and"CaseInsensitive"
keys in URL query string) which succeeds and corresponding values are bound.However if I change the controller action slightly by changing the
dto
parameter name toterm
to match theTerm
property in DTO class:then the binding stops working. Calling the API using the same URL as previously results in controller action being invoked, but the DTO properties are not filled with values from URL, instead they have default values (aka.
Term
property isnull
andCaseInsensitive
isfalse
).Why is that? The binding should evaluate the same way as previously - keys
"term.Term"
and"term.CaseInsensitive"
are not found in URL query string, so unprefixed names should be tried and matched as before, thus I expected the binding to work. It seems very weird that such innocent change completely breaks the binding. We encountered this behavior today and it was not easy to figure out why out of nowhere the binding does not work anymore...Further technical details
dotnet --info Output
.NET SDK (reflecting any global.json): Version: 5.0.401 Commit: 4bef5f3dbfRuntime Environment: OS Name: Windows OS Version: 10.0.18363 OS Platform: Windows RID: win10-x64 Base Path: C:\Program Files\dotnet\sdk\5.0.401\
Host (useful for support): Version: 5.0.10 Commit: e1825b4928
.NET SDKs installed: 5.0.301 [C:\Program Files\dotnet\sdk] 5.0.401 [C:\Program Files\dotnet\sdk]
.NET runtimes installed: Microsoft.AspNetCore.App 3.1.19 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 5.0.7 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 5.0.10 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.NETCore.App 3.1.19 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 5.0.7 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 5.0.10 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.WindowsDesktop.App 3.1.19 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 5.0.7 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 5.0.10 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]