dotnet / dotnet/aspnetcore

ASP.NET Core Web API complex type binding from query does not work if parameter name equals property name

Open
#37,360 7 comments 4 reactions 0 assignees View on GitHub
analyzer area-minimal feature-model-binding
Dominant language
C#
Stars
38.4k
Forks
10.9k
Avg merge
2d 5h
Merged PRs (30d)
276

Description

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:
```csharp
[HttpGet("search")]
public IEnumerable Search([FromQuery] SearchDto dto)
{
// implementation
}
```
the action parameter `dto` is a complex type bound **from query** and it looks like this:
```csharp
public class SearchDto
{
public string Term { get; set; }
public bool CaseInsensitive { get; set; }
}
```
If I call the API using URL `//search?term=abc&caseInsensitive=true` it all works as expected - `Term` property contains `"abc"` and `CaseInsensitive` is `true`.

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 to `term` to match the `Term` property in DTO class:
```csharp
[HttpGet("search")]
public IEnumerable Search([FromQuery] SearchDto term) // changed from 'dto' to 'term'
{
// implementation
}
```
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 is `null` and `CaseInsensitive` is `false`).

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
- ASP.NET Core version: 5.0
- The IDE (VS / VS Code/ VS4Mac) you're running on, and its version: Microsoft Visual Studio Professional 2019 Version 16.11.4

dotnet --info Output
.NET SDK (reflecting any global.json):
Version: 5.0.401
Commit: 4bef5f3dbf

Runtime 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]

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.