Say I have a ViewModel like this coming from a form submit in ASP.NET MVC:
public class EditProfileViewModel
{
public string DisplayName { get; set; }
public IFormFile Photo { get; set; }
}
I can receive this easily enough inside a controller:
public async Task<IActionResult> OnPost([FromForm] EditProfileViewModel edits)
{
// edits contains form data, great
}
But now I would like to forward this to a backend API, also using ASP.NET. Including the file upload. Easiest way to do that seems to be creating a new form POST with HttpClient, using MultipartFormDataContent. But as far as I can tell, while the conversion from form content to model classes happens transparently when receiving requests, creating MultipartFormDataContent requires hard-coding the key-value pairs.
I know it is possible to do something like this:
var form = new MultipartFormDataContent()
form.Add(new StringContent(edits.DisplayName), "displayName")
var content = new StreamContent(edits.Photo.OpenReadStream());
content.Headers.ContentType = MediaTypeHeaderValue.Parse(file.ContentType)
form.Add(content, "photo") // Is this the right capitalization ?? See how error prone this is?
var result = await client.PostAsync("some-api", form);
But this is verbose and error prone with duplicate declarations. Also, to use the same model class to deserialize on the receiving end requires knowledge of the conversion magic.
Is there a better way to convert model classes back into MultipartFormDataContent? Or, in case this is an XY problem, a better way to forward this form model to a backend ASP.NET API altogether?
2
Answers
That’s the point –
MultipartFormDataContent
can be used to post a form to any web application, and the code doesn’t know the underlying parsing magic because it needs to be told how the receiving end expects its format.One web framework can be case-sensitive, while the other requires lowercase or exactly matching case. Or for arrays, one can support repetition of field names (
name="foo"
,name="foo"
, …) the other may require a[]
suffix (name="foo[]"
,name="foo[]"
, …) and yet another may require indices (name="foo[0]"
,name="foo[1]"
, …).Let alone for multiple levels of properties:
name="foo.bar"
,name="foo_bar"
, …So you’ll have to write your own property-to-fieldname-mapping code, knowing what framework you POST to.
I solved ‘hard-coding the key-value pairs’ with some extension methods and reflection :
Usage:
In this example , there is no diffrence between ViewModel or Dto , So you can use ViewModel