Post JSON with data AND file to Web Api - jQuery / MVC

42,808

You can't upload a file(that is arbitrary binary data) with JSON as JSON is a text format. you'll have to use multipart form data.

// create model for controller
var model = new FormData();
model.append('Name', $.trim($contestForm.find('[name="nombre"]').val()) + ' ' + $.trim($contestForm.find('[name="apellido"]').val()));
model.append('Email', $.trim($contestForm.find('[name="email"]').val().toLowerCase()));
model.append('City', $.trim($contestForm.find('[name="cuidad"]').val()));
model.append('Title', $.trim($contestForm.find('[name="title"]').val()));
model.append('Description', $.trim($contestForm.find('[name="description"]').val()));
model.append('CountryCode', 'co');
model.append('Image', $contestForm.find('[name="file-es"]')[0].files[0]);  // this has the file for sure

$.ajax({
    url: '/Umbraco/api/ControllerName/CreateContestEntry',
    type: 'POST',
    dataType: 'json',
    data: model,
    processData: false,
    contentType: false,// not json
    complete: function (data) {
        var mediaId = $.parseJSON(data.responseText); //?

    },
    error: function (response) {
        console.log(response.responseText);
    }
});
Share:
42,808
Rob Scott
Author by

Rob Scott

Sitecore, Umbraco, C#, APIs, REST, VSTS, Azure DevOps, CI/CD, SCSS, jQuery/JS, Git, Gulp

Updated on April 14, 2020

Comments

  • Rob Scott
    Rob Scott about 4 years

    I need to post to an Api Controller w/ JSON (preferably) with ONE request.

    The issue is passing data AND a file (image uploaded). My property is coming up empty (null).

    I've looked at quite a bit of blogs but can't seem to get the image's data passed.

    public class SomeModel
    {
        public string Name { get; set; }
        public string Email { get; set; }
        public string City { get; set; }
        public HttpPostedFileBase Image { get; set; }
        public string Title { get; set; }
        public string Description { get; set; }
        public string CountryCode { get; set; }
    }
    
    
        [HttpPost]
        public void CreateContestEntry(SomeModel model)
        {
            // model.Image is always null
            // .. get image here - the other properties no issues
        }
    

    jQuery

        // create model for controller
        var model = {
            Name: $.trim($contestForm.find('[name="nombre"]').val()) + ' ' + $.trim($contestForm.find('[name="apellido"]').val()),
            Email: $.trim($contestForm.find('[name="email"]').val().toLowerCase()),
            City: $.trim($contestForm.find('[name="cuidad"]').val()),
            Title: $.trim($contestForm.find('[name="title"]').val()),
            Description: $.trim($contestForm.find('[name="description"]').val()),
            CountryCode: 'co',
            Image: $contestForm.find('[name="file-es"]')[0].files[0]  // this has the file for sure
        };
    
        $.ajax({
            url: '/Umbraco/api/ControllerName/CreateContestEntry',
            type: 'POST',
            dataType: 'json',
            data: JSON.stringify(model),
            //data: $('#test-form').serialize(),  // tried this and using FormData()
            processData: false,
            async: false,
            contentType: 'application/json; charset=utf-8',
            complete: function (data) {
    
            },
            error: function (response) {
                console.log(response.responseText);
            }
        });
    

    enter image description here

    Blogs I've looked at:


    When I tried the FormData and $('#form1').serialize() approach, my provider.FileData and provider.FormData were always empty as well. I removed the model param from the method and the breakpoints were hitting when I switched it up.

        [HttpPost]
        public void CreateContestEntry()
        {
            string root = HttpContext.Current.Server.MapPath("~/App_Data");
            var provider = new MultipartFormDataStreamProvider(root);
    
            try
            {
                // Read the form data.
                Request.Content.ReadAsMultipartAsync(provider);
    
                // This illustrates how to get the file names.
                foreach (MultipartFileData file in provider.FileData)
                {
                    // empty
                }
    
                foreach (var key in provider.FormData.AllKeys)
                {
                    foreach (var val in provider.FormData.GetValues(key))
                    {
                        // empty
                    }
                }
                //return Request.CreateResponse(HttpStatusCode.OK);
            }
            catch(Exception ex)
            {
    
            }
        }
    

    SOLUTION:

    Going off of @Musa's answer, here's the Api Controller code. I mapped the NameValueCollection to my model.

        [HttpPost]
        public void CreateContestEntry()
        {
            try
            {
                // get variables first
                NameValueCollection nvc = HttpContext.Current.Request.Form;
                var model = new WAR2015ContestModel();
    
                // iterate through and map to strongly typed model
                foreach (string kvp in nvc.AllKeys)
                {
                    PropertyInfo pi = model.GetType().GetProperty(kvp, BindingFlags.Public | BindingFlags.Instance);
                    if (pi != null)
                    {
                        pi.SetValue(model, nvc[kvp], null);
                    }
                }
    
                model.Image = HttpContext.Current.Request.Files["Image"];
            }
            catch(Exception ex)
            {
    
            }
        }