Is there a way to be able to get model binding (or whatever) to give out the model from a multipart form data request in ASP.NET MVC Web API?
I see various blog posts but either things have changed between the post and actual release or they don’t show model binding working.
This is an outdated post: Sending HTML Form Data
and so is this: Asynchronous File Upload using ASP.NET Web API
I found this code (and modified a bit) somewhere which reads the values manually:
Model:
public class TestModel
{
[Required]
public byte[] Stream { get; set; }
[Required]
public string MimeType { get; set; }
}
Controller:
public HttpResponseMessage Post()
{
if (!Request.Content.IsMimeMultipartContent("form-data"))
{
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
}
IEnumerable<HttpContent> parts = Request.Content.ReadAsMultipartAsync().Result.Contents;
string mimeType;
if (!parts.TryGetFormFieldValue("mimeType", out mimeType))
{
return Request.CreateResponse(HttpStatusCode.BadRequest);
}
var media = parts.ToArray()[1].ReadAsByteArrayAsync().Result;
// create the model here
var model = new TestModel()
{
MimeType = mimeType,
Stream = media
};
// save the model or do something with it
// repository.Save(model)
return Request.CreateResponse(HttpStatusCode.OK);
}
Test:
[DeploymentItem("test_sound.aac")]
[TestMethod]
public void CanPostMultiPartData()
{
var content = new MultipartFormDataContent { { new StringContent("audio/aac"), "mimeType"}, new ByteArrayContent(File.ReadAllBytes("test_sound.aac")) };
this.controller.Request = new HttpRequestMessage {Content = content};
var response = this.controller.Post();
Assert.AreEqual(response.StatusCode, HttpStatusCode.OK);
}
This code is basically fragile, un-maintainable and further, doesn’t enforce the model binding or data annotation constraints.
Is there a better way to do this?
Update: I’ve seen this post and this makes me think – do I have to write a new formatter for every single model that I want to support?
@Mark Jones linked over to my blog post http://lonetechie.com/2012/09/23/web-api-generic-mediatypeformatter-for-file-upload/ which led me here. I got to thinking about how to do what you want.
I believe if you combine my method along with TryValidateProperty() you should be able accomplish what you need. My method will get an object deserialized, however it does not handle any validation. You would need to possibly use reflection to loop through the properties of the object then manually call TryValidateProperty() on each one. This method it is a little more hands on but I’m not sure how else to do it.
http://msdn.microsoft.com/en-us/library/dd382181.aspx
http://www.codeproject.com/Questions/310997/TryValidateProperty-not-work-with-generic-function
Edit: Someone else asked this question and I decided to code it just to make sure it would work. Here is my updated code from my blog with validation checks.