0

I'm trying to send two lists of JSON, a DateTime, int, string, and file to a C# controller via JQuery AJAX POST request.

All data sends fine except the two object arrays, they send nothing.

I've tried changing them from a list of objects to a list of strings to convert from there, but the array is received as one long string, making it nonconvertible via JsonConvert in Newtonsoft.Json.

I've tried logging the formdata objects in order to check their validity, and they seem fine in console. I'm not entirely sure where I've messed up.

Here is my JS:

var formData = new FormData();
formData.append("assignedUsers", JSON.stringify(assignedUsers));
formData.append("ccUsers", JSON.stringify(ccUsers));
formData.append("dueDate", $("#DueDate").val());
formData.append("goalClassID", parseInt(goalClassID));
formData.append("goalDescription", $("#GoalDescription").val());
formData.append("file", document.getElementById("GoalFile").files[0]);

for (var pair of formData.entries()) {
    console.log(pair[0] + ', ' + pair[1]);
}

$.ajax({
    url: api + "main/CreateGoal",
    type: "POST",
    data: formData,
    cache: false,
    dataType: "json",
    processData: false,
    contentType: false,
    success: function (result) {
        if (result) {
            toastr.success("Goal successfully created");
        } else {
            toastr.error("Goal creation failed.");
        }
    }
})

This is my C# Controller:

public bool CreateGoal([FromForm]List<User>AssignedUsers, [FromForm]List<User>CCUsers, [FromForm]DateTime DueDate, [FromForm]int GoalClassID, [FromForm]string GoalDescription, [FromForm]IFormFile File = null)

This is User class

public class User : Base
{
    public string UUID { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Email { get; set; }
    public int Permission { get; set; }
    public string FullName { get { return FirstName + " " + LastName; } }
}
Jake Starr
  • 53
  • 4
  • 11

2 Answers2

2

Both object arrays are empty because you are posting them as json, but all the data is posted as form-data and [FromForm] expects input to be form-data of special format. Everything is working fine for primitive values because they added to FormData like this

formData.append(key, value)

as expected. But when you need to pass an array of objects it must be of the following format

formData.append("obj[0].Field1", field1Val)
formData.append("obj[0].Field2", field2Val)
formData.append("obj[0].Field3", field3Val)
...
formData.append("obj[1].Field1", field1Val)
formData.append("obj[1].Field2", field2Val)
formData.append("obj[1].Field3", field3Val)

So you need to update your code to something like this

var formData = new FormData();
formData.append("assignedUsers[0].uuid", assignedUsers[0].uuid);
formData.append("assignedUsers[0].firstName", assignedUsers[0].firstName);
...
formData.append("assignedUsers[1].uuid", assignedUsers[1].uuid);
formData.append("assignedUsers[1].firstName", assignedUsers[1].firstName);
...
formData.append("dueDate", $("#DueDate").val());
formData.append("goalClassID", parseInt(goalClassID));
formData.append("goalDescription", $("#GoalDescription").val());
formData.append("file", document.getElementById("GoalFile").files[0]);

Well, since this is not very good way to write code you would like to create a function doing it for you. The final solution is

function addUsers(formdata, users, name) {
    for (var i = 0; i < users.length; i++) {
        addSingleUser(formdata, users[i], name + "[" + i + "]");
    }
}

function addSingleUser(formdata, user, name) {
    for (var key in user) {
        formdata.append(name + "." + key,  user[key]);
    }
}

var formData = new FormData();
formData.append("dueDate", $("#DueDate").val());
formData.append("goalClassID", parseInt(goalClassID));
formData.append("goalDescription", $("#GoalDescription").val());
formData.append("file", document.getElementById("GoalFile").files[0]);
addUsers(formData, assignedUsers, "assignedUsers")
addUsers(formData, ccUsers, "ccUsers")

$.ajax({
    url: "/Home/CreateGoal",
    type: "POST",
    data: formData,
    cache: false,
    dataType: "json",
    processData: false,
    contentType: false,
    success: function (result) {
        if (result) {
            toastr.success("Goal successfully created");
        } else {
            toastr.error("Goal creation failed.");
        }
    }
})
Alexander
  • 9,104
  • 1
  • 17
  • 41
0

So due to the way the ajax call needs to handle files it does not also process the models in the array as json objects. So, I made a change where the first method will return the created object and the second method will then accept the file.

C# Controller

[HttpPost("CreateGoal")]
    public Goal CreateGoal(List<User> AssignedUsers, List<User>CCUsers, DateTime DueDate, int GoalClassID, string GoalDescription)
    {
        try
        {
            //Code to save new goal model
            return goal;
        }
        catch (Exception e)
        {

            return null;
        }
    }

    [HttpPost("CreateGoalFile")]
    public bool CreateGoalFile([FromForm] int GoalID, [FromForm]IFormFile File)
    {
        try
        {
            //Code to save file and make database relation
            return true;
        }
        catch (Exception)
        {
            return false;
        }
    }

Javascript solution

$.post(api + "main/CreateGoal",
        { AssignedUsers: assignedUsers, CCUsers: ccUsers, DueDate: $("#DueDate").val(), GoalClassID: parseInt(goalClassID), GoalDescription: $("#GoalDescription").val() },
        function (result) {
            if (document.getElementById("GoalFile").files[0] != null) {
                var formData = new FormData();
                formData.append("goalID", result.id);
                formData.append("file", document.getElementById("GoalFile").files[0]);
                $.ajax({
                    url: api + "main/CreateGoalFile",
                    type: "POST",
                    data: formData,
                    cache: false,
                    dataType: "json",
                    processData: false,
                    contentType: false,
                    success: function (result) {
                        if (result) {
                            toastr.success("File uploaded sucessfully created");
                        } else {
                            toastr.error("File uploaded failed.");
                        }
                    }
                })
            }
            toastr.success("Goal successfully created");
        }
    )
Jake Starr
  • 53
  • 4
  • 11