0

I am trying to post a complex JSON object to my C#/MVC controller. The object looks like this:

public class RootObject
{
    public Guid Id { get; set; }
    public List<ChildObject> Children { get; set; }
}

public class ChildObject
{
     public HttpPostedFileBase UploadedDocument { get; set; }
    // Other properties here, but left out of this example
}

And my controller action looks like this:

    [HttpPost]
    public JsonResult UploadFiles(RootObject dto)
    {
         // Do stuff with root object and each file in each child object.
    }

My JavaScript/jQuery code looks like this:

    var formData = new FormData;
    formData.append('Id', 'some_id');
    var index = 0;
    $('.member').each(function () {
        formData.append('Children[' + index + '][UploadedDocument]', document.getElementById(id).files[0]);
        index++;
    });

    $.ajax({
        type: "POST",
        url: endpointUrl,
        processData: false,
        contentType: false,
        data: formData,
        success: function (responseData) {

        },
        error: function (res) {

        }
    });

This just won't work. C#/MVC does not receive the UploadedDocument. If (for testing purposes only) I move the UploadedDocument property to the RootObject, turn it into a collection of HttpPostedFileBase and post the files directly to the root, then all works, but that is not a solution since the ChildObject has other properties I need to post as well.

Any ideas? My conclusion right now is that it is not possible to post files to children of c# objects via MVC.

Selim Yildiz
  • 5,254
  • 6
  • 18
  • 28
nath
  • 119
  • 1
  • 6
  • 1
    Have you set the form type to multipart/form-data? – ste-fu Jan 16 '20 at 10:12
  • @ste-fu yes I have this form on the page:
    – nath Jan 16 '20 at 10:17
  • The problem is the Javascript code - you can't send a Blob as a JSON attribute like this. – Panagiotis Kanavos Jan 16 '20 at 10:18
  • @PanagiotisKanavos it works when I post it to the RootObject, but not the children. Can you elaborate on your answer? – nath Jan 16 '20 at 10:19
  • You are posting form data, *not* JSON. Forms are flat, you can't have nested input fields. There are no children to begin with. The DTO should contain an `IFormFile[]` property to hold all files. This isn't an MVC restriction – Panagiotis Kanavos Jan 16 '20 at 10:25
  • @PanagiotisKanavos I see, but then the restriction must be in regards to posting files. If I post other properties like string, int and so on it all works even with nested input. As long as I post them in the format like this: formData.append('Children[' + index + '][Id]', 'ID'); – nath Jan 16 '20 at 10:31
  • That's how HTTP works. Files are sent as different parts of the request. That's what `multipart` is for. When you add a Blob either directly or indirectly to a form request, you're adding another part to the request. – Panagiotis Kanavos Jan 16 '20 at 10:33
  • Check [Uploading Multiple Files](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch#Uploading_multiple_files). You can use the *same* field name to post multiple files,eg `documents`, that should match the root property's name. After all,the contents of a multifile input field are posted using the same name. There's no special meaning to field names anyway. `Children[3][UploadedDocument]` is just a weird name – Panagiotis Kanavos Jan 16 '20 at 10:39
  • Do check MDN's code and notice how clean it is. This question's Javascript though probably doesn't work. It's looping over all elements with the `member` class and then trying to retrieve the *same* file with `document.getElementById(id).files[0]`. This will post the *same* file multiple times. – Panagiotis Kanavos Jan 16 '20 at 10:45
  • @PanagiotisKanavos yes it is very clean, and so would mine be if I was trying to post a simple object like they do. I my case I am posting an object with a collection of objects which has file uploads. I have debugged my solution now, and the files are posted to the server, but C# does not map the files to the objects correctly. So everything is posted, but not mapped correctly on the server side. The "document.getElementById(id).files[0]" is just bogus code I added so no worries, I do pick up the correct data. Also Children[3][UploadedDocument] denotes third/3 object in the collection. – nath Jan 16 '20 at 10:56
  • You still miss the point. You're posting form data. Form data is flat. The names you use have no significance, so C# doesn't have to map them to anything. You assume some naming convention that simply doesn't exist. Even in Javascript `Children[3][UploadedDocument]` makes no sense - this mixes array and dictionary indexing. In JavaScript you'd use `Children[3].UploadedDocument` to get the `UploadedDocument` property of the *fourth* element. – Panagiotis Kanavos Jan 16 '20 at 11:00
  • Are you trying to replicate some other language's/server's field naming conventions perhaps? Why do you insist on using those names? – Panagiotis Kanavos Jan 16 '20 at 11:03
  • I see. Well the naming convention does actual enable me to populate a complex object structure in C# by using that specific way of posting data. The only thing C# does not map is the files/binary. All other properties are mapped nicely to their respective properties in the C# object. So yes I understand that the form data is flat, but someone at Microsoft wrote magic that automatically does the mapping. I find it highly unlikely that I am the only person in the world that this works for: http://www.levibotelho.com/development/posting-javascript-objects-with-ajax-and-asp-net-mvc/ – nath Jan 16 '20 at 11:04

0 Answers0