BindingSource for IEnumerable<IFormFile> ApiController parameters on is incorrectly inferred as FromBody #7770
Description
Is this a Bug or Feature request?:
Bug
Steps to reproduce or link to a repro project:
I have a working controller action that receives a List<IFormFile>
and defaults to mvc model binding to upload files to a web api endpoint.
See File Uploads MS docs article which I used as a reference. It's largely irrelevant because it's not even getting into the action method due to a model binding failure.
I test the file upload behavior by specifying the version in my csproj <PackageReference Include="Microsoft.AspNetCore.App" Version="2.1.0-preview1-final"/>
and it works.
Modify Version
attribute from 2.1.0-preview1-final
to 2.1.0-rc1-final
and the endpoint is never invoked. Breakpoints are never hit within the action method. Model binding errors are seen in the debug output when I turn up logging in appSettings.
Description of the problem:
I'm using axios ajax lib to upload FormData() to a web api endpoint:
[HttpPost( "photo" )]
public async Task<IActionResult> SavePhotosToDb( string accountId, List<IFormFile> photos ) {
}
string accountId
binds ok, but fails at model binding on the upload field, by turning up logging I get the following from asp.net core:
Connection id "0HLDMU63FETUR", Request id "0HLDMU63FETUR:00000006": started reading request body.
Microsoft.AspNetCore.Server.Kestrel:Debug: Connection id "0HLDMU63FETUR", Request id "0HLDMU63FETUR:00000006": started reading request body.
dbug: Microsoft.AspNetCore.Server.Kestrel[26]
Connection id "0HLDMU63FETUR", Request id "0HLDMU63FETUR:00000006": done reading request body.
Microsoft.AspNetCore.Server.Kestrel:Debug: Connection id "0HLDMU63FETUR", Request id "0HLDMU63FETUR:00000006": done reading request body.
dbug: Microsoft.AspNetCore.Mvc.ModelBinding.ParameterBinder[22]
Attempting to bind parameter 'accountId' of type 'System.String' ...
Microsoft.AspNetCore.Mvc.ModelBinding.ParameterBinder:Debug: Attempting to bind parameter 'accountId' of type 'System.String' ...
dbug: Microsoft.AspNetCore.Mvc.ModelBinding.Binders.SimpleTypeModelBinder[24]
Attempting to bind model of type 'System.String' using the name 'accountId' in request data ...
Microsoft.AspNetCore.Mvc.ModelBinding.Binders.SimpleTypeModelBinder:Debug: Attempting to bind model of type 'System.String' using the name 'accountId' in request data ...
dbug: Microsoft.AspNetCore.Mvc.ModelBinding.Binders.SimpleTypeModelBinder[25]
Done attempting to bind model of type 'System.String' using the name 'accountId'.
Microsoft.AspNetCore.Mvc.ModelBinding.Binders.SimpleTypeModelBinder:Debug: Done attempting to bind model of type 'System.String' using the name 'accountId'.
dbug: Microsoft.AspNetCore.Mvc.ModelBinding.ParameterBinder[23]
Done attempting to bind parameter 'accountId' of type 'System.String'.
Microsoft.AspNetCore.Mvc.ModelBinding.ParameterBinder:Debug: Done attempting to bind parameter 'accountId' of type 'System.String'.
dbug: Microsoft.AspNetCore.Mvc.ModelBinding.ParameterBinder[22]
Attempting to bind parameter 'photos' of type 'System.Collections.Generic.List`1[Microsoft.AspNetCore.Http.IFormFile]' ...
Microsoft.AspNetCore.Mvc.ModelBinding.ParameterBinder:Debug: Attempting to bind parameter 'photos' of type 'System.Collections.Generic.List`1[Microsoft.AspNetCore.Http.IFormFile]' ...
dbug: Microsoft.AspNetCore.Mvc.ModelBinding.Binders.BodyModelBinder[24]
Attempting to bind model of type 'System.Collections.Generic.List`1[Microsoft.AspNetCore.Http.IFormFile]' using the name '' in request data ...
Microsoft.AspNetCore.Mvc.ModelBinding.Binders.BodyModelBinder:Debug: Attempting to bind model of type 'System.Collections.Generic.List`1[Microsoft.AspNetCore.Http.IFormFile]' using the name '' in request data ...
dbug: Microsoft.AspNetCore.Mvc.ModelBinding.Binders.BodyModelBinder[2]
Microsoft.AspNetCore.Mvc.ModelBinding.Binders.BodyModelBinder:Debug: Rejected input formatter 'Microsoft.AspNetCore.Mvc.Formatters.JsonPatchInputFormatter' for content type 'multipart/form-data; boundary=----WebKitFormBoundaryeuzzmB6yYXHml0Rh'.
Rejected input formatter 'Microsoft.AspNetCore.Mvc.Formatters.JsonPatchInputFormatter' for content type 'multipart/form-data; boundary=----WebKitFormBoundaryeuzzmB6yYXHml0Rh'.
dbug: Microsoft.AspNetCore.Mvc.ModelBinding.Binders.BodyModelBinder[2]
Rejected input formatter 'Microsoft.AspNetCore.Mvc.Formatters.JsonInputFormatter' for content type 'multipart/form-data; boundary=----WebKitFormBoundaryeuzzmB6yYXHml0Rh'.
Microsoft.AspNetCore.Mvc.ModelBinding.Binders.BodyModelBinder:Debug: Rejected input formatter 'Microsoft.AspNetCore.Mvc.Formatters.JsonInputFormatter' for content type 'multipart/form-data; boundary=----WebKitFormBoundaryeuzzmB6yYXHml0Rh'.
dbug: Microsoft.AspNetCore.Mvc.ModelBinding.Binders.BodyModelBinder[3]
No input formatter was found to support the content type 'multipart/form-data; boundary=----WebKitFormBoundaryeuzzmB6yYXHml0Rh' for use with the [FromBody] attribute.
Microsoft.AspNetCore.Mvc.ModelBinding.Binders.BodyModelBinder:Debug: No input formatter was found to support the content type 'multipart/form-data; boundary=----WebKitFormBoundaryeuzzmB6yYXHml0Rh' for use with the [FromBody] attribute.
dbug: Microsoft.AspNetCore.Mvc.ModelBinding.Binders.BodyModelBinder[4]
To use model binding, remove the [FromBody] attribute from the property or parameter named '' with model type 'System.Collections.Generic.List`1[[Microsoft.AspNetCore.Http.IFormFile, Microsoft.AspNetCore.Http.Features, Version=2.1.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60]]'.
Microsoft.AspNetCore.Mvc.ModelBinding.Binders.BodyModelBinder:Debug: To use model binding, remove the [FromBody] attribute from the property or parameter named '' with model type 'System.Collections.Generic.List`1[[Microsoft.AspNetCore.Http.IFormFile, Microsoft.AspNetCore.Http.Features, Version=2.1.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60]]'.
dbug: Microsoft.AspNetCore.Mvc.ModelBinding.Binders.BodyModelBinder[25]
Done attempting to bind model of type 'System.Collections.Generic.List`1[Microsoft.AspNetCore.Http.IFormFile]' using the name ''.
Microsoft.AspNetCore.Mvc.ModelBinding.Binders.BodyModelBinder:Debug: Done attempting to bind model of type 'System.Collections.Generic.List`1[Microsoft.AspNetCore.Http.IFormFile]' using the name ''.
dbug: Microsoft.AspNetCore.Mvc.ModelBinding.ParameterBinder[23]
Done attempting to bind parameter 'photos' of type 'System.Collections.Generic.List`1[Microsoft.AspNetCore.Http.IFormFile]'.
Microsoft.AspNetCore.Mvc.ModelBinding.ParameterBinder:Debug: Done attempting to bind parameter 'photos' of type 'System.Collections.Generic.List`1[Microsoft.AspNetCore.Http.IFormFile]'.
dbug: Microsoft.AspNetCore.Mvc.Infrastructure.ModelStateInvalidFilter[1]
The request has model state errors, returning an error response.
Microsoft.AspNetCore.Mvc.Infrastructure.ModelStateInvalidFilter:Debug: The request has model state errors, returning an error response.
dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[3]
Request was short circuited at action filter 'Microsoft.AspNetCore.Mvc.Infrastructure.ModelStateInvalidFilter'.
Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker:Debug: Request was short circuited at action filter 'Microsoft.AspNetCore.Mvc.Infrastructure.ModelStateInvalidFilter'.
I don't have [FromBody]
attributed to the photos
parameter. I have my controller decorated with:
[ApiController]
[Route( "api/v1/[controller]/{accountId}" )]
public class ProfileController : Controller {
// where the magic happens
}
I haven't made any client-side changes with vue.js + axios, only upgraded project to use dotnet core
2.1 RC1. I followed ASP.Net core blog post for guidance on migration, which didn't touch the api controller or my client-side code.
To add, I hadn't made any changes to client-side before the issue surfaced, only framework versions. While debugging/researching, I did try changing my default header on axios where I default send Content-Type
as application/json
, but modifying that didn't seem to affect anything, and from the model binding errors it appears asp.net is still understanding we're posting multi-part form data.
If I switch between version specifiers, then dotnet clean
, dotnet restore
, and F5
in VS Code - it works under preview1.
dotnet --info
:
.NET Core SDK (reflecting any global.json):
Version: 2.1.300-rc1-008673
Commit: f5e3ddbe73
Runtime Environment:
OS Name: Mac OS X
OS Version: 10.12
OS Platform: Darwin
RID: osx.10.12-x64
Base Path: /usr/local/share/dotnet/sdk/2.1.300-rc1-008673/
Host (useful for support):
Version: 2.1.0-rc1
Commit: eb9bc92051
.NET Core SDKs installed:
2.0.0 [/usr/local/share/dotnet/sdk]
2.1.300-preview1-008174 [/usr/local/share/dotnet/sdk]
2.1.300-rc1-008673 [/usr/local/share/dotnet/sdk]
.NET Core runtimes installed:
Microsoft.AspNetCore.All 2.1.0-preview1-final [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.All]
Microsoft.AspNetCore.All 2.1.0-rc1-final [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.All]
Microsoft.AspNetCore.App 2.1.0-preview1-final [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 2.1.0-rc1-final [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App]
Microsoft.NETCore.App 2.0.5 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App]
Microsoft.NETCore.App 2.1.0-preview1-26216-03 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App]
Microsoft.NETCore.App 2.1.0-rc1 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App]
Version of Microsoft.AspNetCore.Mvc
or Microsoft.AspNetCore.App
or Microsoft.AspNetCore.All
:
2.1.0-preview1-final
2.1.0-rc1-final