IFormFile binding
artyom-p opened this issue · 6 comments
Is it possible to bind IFormFile to a model when posting a form with multiple fields and one or more files?
Currently I am able to bind only form key/values with HybridModelBinding.
Binding works for Id property but not for file
public class SetImageCommand
{
[HybridBindProperty(Source.Form, "UniqueName")]
public string Id { get; set; }
[HybridBindProperty(Source.Form, "MyImage")]
public IFormFile Image { get; set; }
}
Workaround:
public async Task<ActionResult> SetImage([FromForm(Name = "UniqueName")]string id, [FromForm(Name = "MyImage")]IFormFile image)
Updating to https://www.nuget.org/packages/HybridModelBinding/0.16.0 should fix your issue. You will also be able to bind to a collection of files:
public class SetImageCommand
{
[HybridBindProperty(Source.Form, "UniqueName")]
public string Id { get; set; }
[HybridBindProperty(Source.Form, "MyImages")]
public IEnumerable<IFormFile> Images { get; set; }
}
The approach I implemented is a bit brittle since it uses reflection to get the collection of files from the FormValueProvider–which via FormCollection does not directly expose bound-files. I may take a look at a different approach in the future and don't anticipate it affecting the end-user usage.
Perfect, thanks!
Hi @billbogaiv,
I saw the update on your package and this is exactly what I'm searching for but I can't get it to work... If I use a parameter IEnumerable<IFormFile> documents
in my action, it works.
Do you see anything wrong with this setup?
// xunit integration test
[Fact]
public async Task Upload_SavesPhotoAndReturnSuccess()
{
// Arrange
var fileFakerHttpClient = new HttpClient();
var expectedContentType = "text/html; charset=utf-8";
var url = $"api/foos/{_foo1Id}/documents/add";
HttpResponseMessage response = null;
try
{
// Act
using (var file1 = await fileFakerHttpClient.GetAsync(faker.Image.PicsumUrl()))
using (var content1 = new StreamContent(await file1.Content.ReadAsStreamAsync()))
using (var file2 = await fileFakerHttpClient.GetAsync(faker.Image.PicsumUrl()))
using (var content2 = new StreamContent(await file2.Content.ReadAsStreamAsync()))
using (var formData = new MultipartFormDataContent())
{
// Add file (file, field name, file name)
formData.Add(content1, "Documents", file1.Content.Headers.ContentDisposition.FileName);
formData.Add(content2, "Documents", file2.Content.Headers.ContentDisposition.FileName);
response = await _httpClient.PostAsync(url, formData);
}
// Assert
response.EnsureSuccessStatusCode();
var responseString = await response.Content.ReadAsStringAsync();
responseString.ShouldNotBeEmpty();
expectedContentType.ShouldBe(response.Content.Headers.ContentType.ToString());
}
finally
{
fileFakerHttpClient.Dispose();
response.Dispose();
_httpClient.Dispose();
}
}
// model. In FooBaseDto there only more route parameters that I removed for clarity. Those parameters work
public class AddDocumentByFooDto : FooBaseDto
{
[HybridBindProperty(Source.Route)]
public Guid FooId { get; set; }
[HybridBindProperty(Source.Form, "Documents")]
public IEnumerable<IFormFile> Documents { get; set; }
}
// Action where model.Documents is always null
[HttpPost("api/foos/{fooId}/documents/add")]
public async Task<IActionResult> AddDocumentByFoo([FromHybrid]AddDocumentByCaseDto model, CancellationToken cancellationToken)
I don't know what went wrong. Everything is working now without a code change... Your postman worked as well. Thanks.