How to accept file + data in MVC5 Web API?

Stesvis 1,041 Reputation points
2023-02-22T23:50:41.1166667+00:00

Hello,

I am sending a POST request to my API endpoint. This is a MVC5 question, not .NET Core.
The payload includes a file and some data:

const formData = new FormData();
formData.append("file", file);
formData.append("data", data);

axios.post(url, formData, {
  headers: {
        'Content-Type': 'multipart/form-data'
  }
});

My problem is that I am unable to make the API work. I tried this:

[HttpPost]
[Route("Import")]
[Authorize]
public async Task<IHttpActionResult> Import(ImportCustomerRequest request)
{
    //...
    return Ok();
}

Where

    public class ImportCustomersRequest
    {
        public HttpPostedFileBase File { get; set; }
 
        public string Data { get; set; }
    }

I always get "415 Unsupported Media Type" when I try to consume this API.

{
  "message": "The request entity's media type 'multipart/form-data' is not supported for this resource.",
  "exceptionMessage": "No MediaTypeFormatter is available to read an object of type 'ImportCustomersRequest' from content with media type 'multipart/form-data'.",
  "exceptionType": "System.Net.Http.UnsupportedMediaTypeException",
  "stackTrace": "   at System.Net.Http.HttpContentExtensions.ReadAsAsync[T](HttpContent content, Type type, IEnumerable`1 formatters, IFormatterLogger formatterLogger, CancellationToken cancellationToken)\r\n   at System.Web.Http.ModelBinding.FormatterParameterBinding.ReadContentAsync(HttpRequestMessage request, Type type, IEnumerable`1 formatters, IFormatterLogger formatterLogger, CancellationToken cancellationToken)"
}

Can anyone help me define the API method to accept a file + some data?

Thanks!

Developer technologies ASP.NET ASP.NET API
Developer technologies ASP.NET Other
0 comments No comments
{count} votes

Accepted answer
  1. Lan Huang-MSFT 30,186 Reputation points Microsoft External Staff
    2023-02-23T06:26:45.2366667+00:00

    Hi @Stesvis ,

    I was able to get rid of the original error by adding this to WebApiConfig.cs:

    This only makes ASP.NET route multipart/form-data requests to your controllers. It won't be able to deserialize the form values and bind them to your method's parameters.

    You can upload files to the API directly through the HTML form.

    Sending HTML Form Data in ASP.NET Web API: File Upload and Multipart MIME

    <form name="form1" method="post" enctype="multipart/form-data" action="/api/test">
        <div>
            <label for="Data">Data</label>
            <input name="Data" type="text" />
        </div>
        <div>
            <label for="File"> File</label>
            <input name="File" type="file" />
        </div>
        <div>
            <input type="submit" value="Submit" />
        </div>
    </form>
    
    public class TestController : ApiController
        {
            [HttpPost]
            public async Task<HttpResponseMessage> PostFormData()
            {
                // Check if the request contains multipart/form-data.
                if (!Request.Content.IsMimeMultipartContent())
                {
                    throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
                }
    
                string root = HttpContext.Current.Server.MapPath("~/App_Data");
                var provider = new MultipartFormDataStreamProvider(root);
    
                try
                {
                    // Read the form data.
                    await Request.Content.ReadAsMultipartAsync(provider);
    
                    // This illustrates how to get the file names.
                    foreach (MultipartFileData file in provider.FileData)
                    {
                        Trace.WriteLine(file.Headers.ContentDisposition.FileName);//get FileName
                        Trace.WriteLine("Server file path: " + file.LocalFileName);//get File Path
                    }
                    return Request.CreateResponse(HttpStatusCode.OK, "pass upload file successed!");
                }
                catch (System.Exception e)
                {
                    return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, e);
                }
            }
        }
    

    TEST

    Best regards,
    Lan Huang


    If the answer is the right solution, please click "Accept Answer" and kindly upvote it. If you have extra questions about this answer, please click "Comment".
    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.


1 additional answer

Sort by: Most helpful
  1. SurferOnWww 4,631 Reputation points
    2023-02-23T01:37:17.4433333+00:00

    Can the following sample help?

    Model

    
    public class UploadModels
    {
        public string CustomField { get; set; }
        public HttpPostedFileBase PostedFile { get; set; }
    }
    

    View

    @model Mvc5App.Controllers.UploadModels
     
    @{
        ViewBag.Title = "Upload";
        Layout = "~/Views/Shared/_Layout.cshtml";
    }
     
    <h2>Upload</h2>
     
    @using (Html.BeginForm("Upload", "Home", FormMethod.Post,
                    new { enctype = "multipart/form-data" }))
    {
        @Html.AntiForgeryToken()
    
        <input type="file" name="postedfile" />
        <button type="submit">Upload by Submit</button>
        <br />
        @ViewBag.Result
    }
    <br />
    <input type="button" id="ajaxUpload" value="Ajax Upload" />
    <br />
     
    <div id="result"></div>
     
     
    @section Scripts {
      <script type="text/javascript">
        //<![CDATA[
        $(function () {
          $('#ajaxUpload').on('click', function (e) {
            var fd = new FormData(document.querySelector("form"));
     
            fd.append("CustomField", "This is some extra data");
     
            $.ajax({
              url: '/home/upload',
              method: 'post',
              data: fd,
              processData: false,
              contentType: false
              }).done(function(response) {
                $("#result").empty;
                $("#result").text(response);
              }).fail(function( jqXHR, textStatus, errorThrown ) {
                $("#result").empty;
                $("#result").text('textStatus: ' + textStatus +
                                ', errorThrown: ' + errorThrown);
              });
          });
        });
        //]]>
      </script>
    }
    

    Controler / Action Method

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Web.Mvc;
    using System.ComponentModel.DataAnnotations;
    using Mvc5App.Models;
    using System.IO;
     
    namespace Mvc5App.Controllers
    {
      public class HomeController : Controller
      {
        public ActionResult Upload()
        {
          return View();
        }
     
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Upload(UploadModels model)
        {
          string result = "";
          HttpPostedFileBase postedFile = model.PostedFile;
          if (postedFile != null && 
              postedFile.ContentLength > 0)
          {
            string filename = 
                      Path.GetFileName(postedFile.FileName);
     
            string path = Server.MapPath("~/UploadedFiles") + 
                          "\\" + filename;
     
            postedFile.SaveAs(path);
     
            result = filename + 
                     " (" + postedFile.ContentType + ") - " +
                     postedFile.ContentLength.ToString() + 
                     " bytes uploaded";
          }
          else
          {
            result = "fail";
          }
     
          if (Request.IsAjaxRequest())
          {
            return Content(result);
          }
          else
          {
            ViewBag.Result = result;
            return View();
          }
        }
      }
    }
    

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.