Sending & Receiving Multipart Request

When posting multiple types of data (image, JSON, querystring, etc) to an API, you can either create multiple requests and process each data type separately or you can roll them into one and post them altogether as a multipart request. For a project that I did recently, I chose the latter. There are several benefits of rolling everything into a single call:

  • Fewer server trips
  • Less code to tie each data together
  • More elegant

The second item is the most compelling reason to me. I needed to upload multiple files and save a corresponding metadata for each file. Uploading the files and posting the data separately would’ve been messy since I needed to write persistence code just to relate each file to its corresponding data. MultipartFormDataContent is just the thing I needed. Collecting the data is very straightforward. Reading it from the ASP.NET side can be tricky, however.

Sending from the Client 

Collecting the data is basically just adding them as part of MultipartFormDataContent‘s collection. It can have several types as long as they are derived from the HttpContent class.

            //Create a multi-part content
            var multiData = new MultipartFormDataContent();
 
            //Add all the attachments
            files.ToList()
                 .ForEach(file => {
                     var fileContent = new StreamContent(file.Open(FileMode.Open));
                     fileContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data") {
                                                                 Name       = "files",
                                                                 FileName   = "\"" + file.Name + "\""
                                                             }; 
                     fileContent.Headers.ContentType = new MediaTypeHeaderValue("image/jpeg");
                     multiData.Add(fileContent, "files");
                 });
 
            //Add the non-file data and serialise them
            multiData.Add(SerializeContent(disputes), "data");
 
            //Post altogether. Viola!
            return await Post<IEnumerable>("url/end-point", multiData);

Receiving the Data

As I mentioned, receiving the data can be a little tricky. There are several approach. One of them is to read the Request stream and parse it. I tried this and ended up following a series of requirements that would ultimately force me to upgrade multiple packages in my solution. It’s not an option so I kept poking.

Fortunately, ASP.NET makes it easy to access all content of a request. For example, posted files are exposed via Request.Files. It will also be automatically binded if you add an IEnumerable<HttpPostedFileBase> parameter to your handler. Additionally, you can access other data via Request.Form and Request.QueryString collections. In my case, the JSON data is accessible via the Request.Form[“data”] key.

        [HttpPost]
        public async Task ReceiveData(IEnumerable<HttpPostedFileBase> files) {
            try {
                return await Task.Run(() => {
                    //Get the files
                    files.ToList().ForEach(file => {
 
                        file.SaveAs(Path.Combine(savePath, filename));
                    });
 
                    //Get the data (which in the form of JSON)
                    var disputes = JsonConvert.DeserializeObject<IEnumerable>(Request.Form["data"]).ToList();
                    return Json(markedDisputes);
                });
            } catch (Exception exception) {
                return JsonError(exception.Message);
            }
        }

Lastly, I am trying to find a way through model binding so that data is automatically received as a parameter of the handler. I’ll do it in a separate post.