Posting serialized form data AND additional data to MVC controller?

asp.net-mvc, c#, javascript

Solution

You might find the `following plugin` useful.

Here's how it might be useful to you. Let's start by cleaning your controller action by defining a view model:

public class MyViewModel
{
    public ExistingOrderDetailsModel Order { get; set; }
    public ExistingTaskDetailsModel Task  { get; set; }
    public DeviceDetailsModel Device  { get; set; }

    public AdditionalRowsViewModel[] AdditionalRows { get; set; }
}

In this example the `AdditionalRowsViewModel` will obviously hold the additional information you are trying to pass to the controller action.

And then your controller action will become:

[HttpPost]
public ActionResult SaveAllDetails(MyViewModel model)
{
    ....
}

OK, this step was absolutely necessary, it's just that when I see a controller action taking more than 1 parameter I simply define a view model.

And finally let's adapt our AJAX call:

$.ajax({
    url: 'SaveAllDetails',
    type: 'POST',
    contentType: 'application/json',
    data: JSON.stringify({
        order: $('form#existingOrderDetailsForm').serializeObject(),
        task: $('form#existingTaskDetailsForm').serializeObject(),
        device: $('form#existingDeviceDetailsForm').serializeObject(),
        additionalRows: subcomponentsDetailsView.getAllGridData()
    }),
    success: function(result) {
        // do something with the result of the AJAX call here
    }
});

Things to notice:

- Get rid of this `dataType: 'json'` parameter in your AJAX request. You are using ASP.NET MVC and hopefully you are returning a Jsonresult from your controller action which is successfully setting the `Content-Type` response header to the correct value. jQuery is intelligent enough to use the value of this response header and pre-process the `result` variable that will be passed to the `success` callback of your AJAX request. So in this case you will already get a javascript object

- Since you are sending JSON to the server in your AJAX request you need to properly specify the `contentType: application/json` parameter. Otherwise how do you expect that ASP.NET MVC will know that the client is sending JSON and apply the correct model binder? By the way the default Content-Type request header that jQuery will send is `application/x-www-form-urlencoded`, so if you are sending JSON payload in your POST request that would be a conflict and violation of the protocol.

Problem

I'm trying to tack on some additional data a POST request sent to my server. Originally, I was sending just several forms worth of information: ``` $.ajax({ url: 'SaveAllDetails', type: 'POST', data: $('form').serialize(), dataType: 'json' }); ``` and the MVC Controller method: ``` [HttpPost] public ActionResult SaveAllDetails([Bind(Prefix = "order")]ExistingOrderDetailsModel existingOrderDetailsModel, [Bind(Prefix = "task")]ExistingTaskDetailsModel existingTaskDetailsModel, [Bind(Prefix = "device")]DeviceDetailsModel deviceDetailsModel) { .... } ``` This works great. MVC's model binder is able to correctly de-serialize the URL-encoded string. Now, requirements have changed. I need to send an additional array of data along with my three forms. This array of data is not held within a form and does not have a binding prefix. I need to do this all in the same Controller method because all validation needs to be performed inside of a single transaction. So, I've now got: ``` var subcomponentsGridRows = JSON.stringify(subcomponentsDetailsView.getAllGridData()); var existingOrderDetailsFormData = $('form#existingOrderDetailsForm').serialize(); var existingTaskDetailsFormData = $('form#existingTaskDetailsForm').serialize(); var deviceDetailsFormData = $('form#existingDeviceDetailsForm').serialize() $.ajax({ url: 'SaveAllDetails', type: 'POST', data: { existingOrderDetailsModel: existingOrderDetailsFormData, existingTaskDetailsModel: existingTaskDetailsFormData, deviceDetailsModel: deviceDetailsFormData, subcomponentsGridRows: subcomponentsGridRows }, dataType: 'json' }); ``` This doesn't work for at least one reason. Each form is represented as a URL-encoded string. subcomponentsGridRows is a JSON structure. The MVC model binder isn't capable of deciphering both types of information in one go as far as I can tell. What's a good way to go about tackling this problem?

Original source

Related problems