3

I've been wracking my brain and the various SO posts on the same topic (see JQuery Ajax and Posting multiple complex objects to asp.net MVC Controller, Passing multiple objects to my controller, etc) but I'm having trouble getting multiple objects to post from an Ajax call to my controller (.NET Core). I believe I have set up my code like this tutorial has (http://www.dotnetcurry.com/aspnet-mvc/1253/multiple-model-objects-input-controller-action-methods) and have also used suggestions from here: http://andrewlock.net/model-binding-json-posts-in-asp-net-core/ to no avail.

The issue: I can only get the first, but not the second, object to bind.

Controller method:

[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Apply(ApplicationInfoViewModel info, SRAgreementViewModel agreement)
{
     if(ModelState.IsValid){
     //do stuff. ModelState.IsValid returns false here.
     }
}

Here's my JS:

$(function () {
    $("#ApplyBAOForm").submit(function (e) {        
        var jsonObj = $('#ApplyForm').serialize();
        var srFormJsonObj = $('#SRForm').serialize();

        $.ajax({
            url: "ApplyBao/Apply",
            datatype: "json",
            contentType: "application/json; charset=utf-8",
            data: { info: jsonObj, agreement: srFormJsonObj },
            type: "POST"
        }).done(function (result) {
            console.log("Apply success: " + result);
            if (result.type === "success") {
                 //hooray  
            }
            else {
                //boo                
            }
        });

    });
});

So this is a bit of an odd situation: there's actually data from two separate forms here. The SRform data is validated prior to submitting the ApplyForm (it is in a modal), but is only actually submitted for persistence with the ApplyForm. The resulting serialized objects from both forms appears to be correct and similar (jsonObj and srFormObj).

When I hit the ModelState.IsValid statement, it returns false because the SRAgreement object is essentially null (with default values). However, the ApplicationInfoViewModel object correctly shows data from the form.

Other things I've tried:

  • combining the two objects into one view model (both child objects wound up being null?)
  • Adding the SRAgreement object as a child of the ApplicationInfo object; the SRAgreement object was null.
  • adding [FromBody] as described by the blog postfrom Andrew Lock, this jsut resulted in an HTTP 415 unsupported media type response

There's likely a better way to do this, but I would think that what I'm attempting to do should be doable.

Thanks!

EDIT 2:

It appears that I can't even bind one JSON object. I've got to be missing something simple. I've pared it down to just submitting one of the two objects as JSON as such:

[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Apply([FromBody] ApplicationInfoViewModel info) 
{
    if (ModelState.IsValid)
    {
     // do stuff
    }
 }

JS:

$("#ApplyBAOForm").submit(function (e) {
        e.preventDefault();

        var jsonObj = ConvertFormToJSON($("#ApplyBAOForm"));
        var srFormJsonObj = ConvertFormToJSON($("#SRForm"));
        var obj2 = JSON.stringify({ info: jsonObj });

        $.ajax({
            url: "ApplyBao/Apply",
            datatype: "json",
            contentType: "application/json; charset=utf-8",
            data: obj2,
            //data: JSON.stringify(jsonObj),
            type: "POST"
        }).done(function (result) {
            console.log("Apply success: " + result);

            }
            else {
                //show some error message or something                
            }
        });        
    });
});

Now the request looks like this:

Request URL:http://localhost:19194/ApplyBao/Apply
Request Method:POST
Status Code:400 Bad Request
Remote Address:[::1]:19194
Response Headers
view parsed
HTTP/1.1 400 Bad Request
Server: Kestrel
X-SourceFiles: =?UTF-8?B?QzpcVXNlcnNccmdiMDA3M1xEb2N1bWVudHNcYmFvYWRtaW5pc3RyYXRpb25cc3JjXEJBT0FkbWluaXN0cmF0aW9uXEFwcGx5QmFvXEFwcGx5?=
X-Powered-By: ASP.NET
Date: Thu, 12 Jan 2017 21:59:27 GMT
Content-Length: 0
Request Headers
view parsed
POST /ApplyBao/Apply HTTP/1.1
Host: localhost:19194
Connection: keep-alive
Content-Length: 580
Pragma: no-cache
Cache-Control: no-cache
Accept: */*
Origin: http://localhost:19194
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36
Content-Type: application/json; charset=UTF-8
Referer: http://localhost:19194/ApplyBao
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.8
Cookie: .AspNetCore.Antiforgery.l7Q8WGuEtq8=CfDJ8K9L5D4kNnBPrb3_9Hu-w57rjjf751Qf9eh-z57qWmtMA53AB6FXsZ7pbIuLkdP2F6GjA7TGl0Tz7TfACCn3QeFt_uC-BgsYnk3Nd8z0ZA0c90XVEA90NnQOnmVFRu_KF2_2DXV89Jur84rMa-s26nQ
Request Payload
view parsed
{"info":{"HearAboutUsSelectedId":"137","FirstName":"Ron","LastName":"Bud","MiddleName":"","MaidenName":"","Email":"[email protected]","BirthDate":"1900-01-15","Phone":"123456","Fax":"","Street":"1234","City":"Denton","State":"TX","Zip":"12345","Country":"USA","SelectedExamTrack":"BCaBA","SelectedTranscriptSendMethod":"requested","Degree":"Education","SraId":"00000000-0000-0000-0000-000000000000","__RequestVerificationToken":"CfDJ8K9L5D4kNnBPrb3_9Hu-w56wPCITEDVZQo7flUIB70Pu4Q81TlRXa_oI4t8Bleou6l45oHHmUFrKusKofA6Gey-uSgKP7M3L-DrawE1TVJnrDsULHlnOE9ngg9LuyFK6-cylBJ-91h5fmaico-0yrZE"}}

The JSON now looks like a proper JSON object, but it seems my princess is in another castle. Is there something else in the pipeline that I'm missing to enable this?

UPDATE:

I finally figured it out. Turns out that the AntiForgery token was causing the request to fail, as mentioned here: POSTing complex JSON to ASP.NET Core Controller Removing that for the moment is allowing me to move forward. Thanks!

2
  • 1
    Wrap the two objects inside a view model and just receive that one wrapper object. Also, since you are .net core, you must have the [FromBody]. Commented Jan 12, 2017 at 19:42
  • I've tried using the [FromBody] with a wrapper object, but I'm now getting "Failed to load resource: the server responded with a status of 415 (Unsupported Media Type)" . I'll look into this error more. Commented Jan 12, 2017 at 19:53

2 Answers 2

3

You are not actually sending 2 objects, you are sending 1 object which contains 2 other objects. That is what this line is, the creation of a single object that will be sent.

data: JSON.stringify( { info: jsonObj, agreement: srFormJsonObj } ), // this is a single object that is created and sent by the http action

You need to receive it the same way on the server with a single model that then contains the 2 types you are currently using as the method parameters.

Model code

public class Model{
    public ApplicationInfoViewModel Info{get;set;} 
    public SRAgreementViewModel Agreement {get;set;}
}

Controller code

public IActionResult Apply([FromBody]Model model)
{
     if(ModelState.IsValid){
     //do stuff. ModelState.IsValid returns false here.
     }
}

Edit

The error is probably because you are sending both url encoded data and json and in a json object. You should use one or the other but not a combination of both. If you look at your latest update you can see the values being sent for properties info and agreement and they are form encoded string values.

Sign up to request clarification or add additional context in comments.

18 Comments

I did try that (and just tried again). Unfortunately, both child objects of the Model object end up being null. Any idea why?
@RGB - do you have the FromBody attribute (see edit)?
I have tried that as well, but I'm now getting "Failed to load resource: the server responded with a status of 415 (Unsupported Media Type)" instead. I'm still trying to figure out what to do with that.
Put JSON.stringify(...) around your model in the ajax call.
@RGB - of interest is the content-type header. See also stackoverflow.com/a/34685114/1260204
|
1

I also stumbled across this problem with .NET Core 2.1

It seems that only one parameter can be passed with the [FromBody] attribute. You have to summarize all parameters in one single object.

Example Ajax call:

 var data = {
                        messageIds: selectedIds,
                        read: true
                    };
                    $.ajax({
                        url: "@Url.Action("SetMessagesReadFlag", "Message")",
                        type: "POST",
                        contentType: 'application/json; charset=utf-8',
                        dataType: 'json',
                        data: JSON.stringify(data),
                        cache: false,
                        error: function(xhr, status, error) {
                            //do something about the error
                            alert(error);
                        },
                        success: function(response) {
                            if (response != null) {
                                grid.refresh();
                            }
                        }
                    });

Controller action with Dummy helper class:

[HttpPost]

public IActionResult  SetMessagesReadFlag([FromBody] Dummy data)
{
    ....
    return Json(true);
}

public class Dummy
{
    public List<Guid> MessageIds { get; set; }

    public bool Read { get; set; }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.