Ajax Call, Multiple Object Parameters, Null at Controller - ajax

I'm pulling my hair out. I can get this to work with one object coming in but when I pass two objects in it fails. I know it is something stupid.
Simple POCO:
public class Test
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
Here is my controller:
[HttpPost]
[AllowAnonymous]
[IgnoreAntiforgeryToken]
public IActionResult GetTestAjaxCallWithMultiObjsIn([FromBody]Test input, Test inputAnother)
{
return Json(string.Concat(input.FirstName, inputAnother.LastName));
}
Both Input and inputAnother are null from JS
Everything from JS
var input = { FirstName: 'Bart', LastName: 'Simpson' };
var inputAnother = { FirstName: 'Bart', LastName: 'Simpson' };
$.ajax({
url: '/Home/GetTestAjaxCallWithMultiObjsIn',
type: "POST",
dataType: 'json',
contentType: 'application/json; charset=utf-8',
data: JSON.stringify({input: input, inputAnother: inputAnother}),
success: function (response) {
console.dir(response);
}
});
What is wrong with the data: stringify???
Thanks tons!
Using .NET CORE 3.1 if it matters
Request Payload
{input: {FirstName: "Bart", LastName: "Simpson"},…}
input: {FirstName: "Bart", LastName: "Simpson"}
inputAnother: {FirstName: "Bart", LastName: "Simpson"}
To be clear: I am trying to pass to different objects in as Params
[HttpPost]
[AllowAnonymous]
[IgnoreAntiforgeryToken]
public IActionResult GetTestAjaxCallWithMultiObjsIn(Test input, Testing inputAnother)
{
return Json(string.Concat(input.FirstName, inputAnother.LastName));
}

You can't send multiple distinct objects and bind them to multiple different params. With [FromBody] the entire request body will be bound to that param. If you need to send multiple things, then you should bind to an enumerable of that thing, and send the JSON as an array of objects.

Related

Parameters not populating with Ajax Call

This is my controller
[HttpPost]
public bool Sync(int? id, string name)
{
throw new NotImplementedException();
}
Here is my ajax request call that I am trying to make to this controller:
<script type="text/javascript">
var buttonClicked = document.getElementById("syncLegacy");
buttonClicked.addEventListener('click', function () { syncLegacyMake(); }, false);
function syncLegacyMake() {
$.ajax({
url: '/Legacy/Sync',
type: 'POST',
data: JSON.stringify({
id: $("#Id").val(),
name: $("#Name").val()
}),
contentType: 'application/json; charset=utf-8',
success: function (data) {
},
error: function () {
alert("error");
}
});
}
The controller gets hit however there are no values to the parameters. The values are both null.
When I look at the call itself on chrome console, the values are populated as these under Request Payload in the headers:
{id: "01", name: "Titan"}
id
:
"01"
name
:
"Titan"
Could anyone point out what I am doing wrong here? I have been able to do the same in .net 4.6.1 framework so not sure if framework changed has caused this?
have you tried the following things:
Using a Dto instead of separate simple types:
public class SyncDto
{
public int? Id {get;set;}
public string Name {get;set;}
}
// Controller action:
[HttpPost]
public bool Sync(SyncDto input)
{
throw new NotImplementedException();
}
Make Jquery stringify itself
Let jquery figure your ajax call out itself:
$.ajax({
url: '/Legacy/Sync',
type: 'POST',
data: {
id: $("#Id").val(),
name: $("#Name").val()
}
});

MVC Controller post json does not work

I have the this code to post json to a controller.
The problem is that the credentials object does not get populated with the posted values.
How do I change this code so that it works?
I see in Fiddler that the request is being posted correctly.
[HttpPost]
public JsonResult Authenticate(CredentialsModel credentials)
{
return Json(credentials);
}
[DataContract]
public class CredentialsModel
{
[DataMember(Name = "user")]
public string User;
[DataMember(Name = "pass")]
public string Pass;
}
$.ajax({
type: "POST",
url: "/login/authenticate",
cache: false,
contentType: "application/json; charset=utf-8",
data: '{"user":' + JSON.stringify($('#username').val()) + ',"uass":' + JSON.stringify($('#userpass').val()) + '}',
dataType: "json",
timeout: 100,
success: function (msg) {
},
complete: function (jqXHR, status) {
if (status == 'success' || status == 'notmodified') {
var obj = jQuery.parseJSON(jqXHR.responseText);
}
},
error: function (req, status, error) {
}
});
The default MVC model binder only works with properties. Your CredentialsModel is using fields. Try changing them to properties. You can also remove the annotations.
public class CredentialsModel
{
public string User { get; set; }
public string Pass { get; set; }
}
Also, as pointed out by Sahib, you can create a Javascript Object and then stringify it, rather than stringifying each one. Although that doesn't appear to be the problem in this case.
data: JSON.stringify({
User: $('#username').val(),
Pass: $('#userpass').val()
})
Try chaning your data like this :
$.ajax({
.................
//notice the 'U' and 'P'. I have changed those to exactly match with your model field.
data: JSON.stringify({User: $('#username').val(),Pass: $('#userpass').val()}),
.................
});

Nested models don't bind

I want to pass a JSON data structure to an MVC (3) Controller, have the JSON object be translated into a C# object, with all properties bound. One of the properties is a simple Type. That's basic model binding, right?
Here are my models:
public class Person
{
public string Name { get; set; }
public JobTitle JobTitle { get; set; }
}
public class JobTitle
{
public string Title { get; set; }
public bool IsSenior { get; set; }
}
Here is my Index.cshtml page (which makes an AJAX request, passing in a JSON object which matches the strcture of the "Person" class):
<div id="myDiv" style="border:1px solid #F00"></div>
<script type="text/javascript">
var person = {
Name: "Bob Smith",
JobTitle: {
Title: "Developer",
IsSenior: true
}
};
$.ajax({
url: "#Url.Action("ShowPerson", "Home")",
data: $.param(person),
success: function (response){
$("#myDiv").html(response);
},
error: function (xhr) {
$("#myDiv").html("<h1>FAIL</h1><p>" + xhr.statusText + "</p>");
}
});
</script>
And my HomeController looks like this:
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
public ActionResult ShowPerson(Person person)
{
return View(person);
}
}
Ignore the "ShowPerson.cshtml" file for now because the problem occurs before that is ever needed.
In the HomeController.ShowPerson action, the "person.Name" property is correctly bound and the "person.JobTitle" object (containing "Title" and "IsSenior" properties) is instantiated but still has the default values of "Title = null" and "IsSenior = false".
I'm sure I have done nested model binding without problem in the past. What am I missing? Can anybody shed any light on why model binding only seems to work one level deep?
I've searched SO, and it seems everybody else is having binding problems when sending data from forms, so maybe I'm missing something in my $.ajax() request?
ok, there are couple of changes you need to do,
Set dataType as json
Set the contentType as application/json; charset=utf-8.
Use JSON.stringify()
below is the modified code. (tested)
var person = {
Name: "Bob Smith",
JobTitle: {
Title: "Developer",
IsSenior: true
}
};
var jsonData = JSON.stringify(person);
$.ajax({
url: "#Url.Action("ShowPerson", "Home")",
data: jsonData,
dataType: 'json',
type: 'POST',
contentType: "application/json; charset=utf-8",
success: function (response){
$("#myDiv").html(response);
},
error: function (xhr) {
$("#myDiv").html("<h1>FAIL</h1><p>" + xhr.statusText + "</p>");
}
});
Add the content type to the ajax
contentType: "application/json; charset=utf-8",
dataType: 'json',
type: 'POST',
data: $.toJSON(person);

AJAX & ASP.NET MVC 3: How to pass a string parameter AND an array at the same time to Controller

I have the following Controller signature:
public void DoSomething(string dialerJob, MyViewModel[] agentStates)
The viewModels represent form fields in an array (selected items in an HTML table). I figured out how to pass the form elements int as an array argument to the controller thanks to Robert Koritnik's .toDictionary() jQuery plug-in (http://erraticdev.blogspot.com/2010/12/sending-complex-json-objects-to-aspnet.html).
However, now I need to pass one additional string parameter (from a dropdown) to the controller and I cannot figure out how to make that work. I've tried various combinations, like:
var job = $('#DialerJobs').attr('value');
var data1 = $.toDictionary(data, "agentStates");
$.ajax({
url: "/Blending/ChangeOutboundJob",
type: "POST",
dataType: "application/JSON",
data: {job, data1}
});
I've also tried the following:
var job = $('#DialerJobs').attr('value');
var data1 = $.toDictionary(data, "agentStates");
$.ajax({
url: "/Blending/ChangeOutboundJob",
type: "POST",
dataType: "application/JSON",
data: {dialerJob: job, agentStates: data1}
});
But neither work.
If I remove the dialerJob parameter from the data to send, the agentStates populate in the controller correctly. And what gets sent looks like this:
agentStates[0].agentId=7654&agentStates[0].projectId=999&agentStates[0].stateId=1&agentStates
[0].subStateId=1&agentStates[1].agentId=9876&agentStates[1].projectId=999&agentStates
[1].stateId=1&agentStates[1].subStateId=1
But if I included the dialerJob, then what gets sent is:
dialerJob=SomeJob&agentStates[0][name]=[0].agentId&agentStates[0][value]=84&agentStates[1][name]=
[0].projectId&agentStates[1][value]=999&agentStates[2][name]=[0].stateId&agentStates[2][value]
=1&agentStates[3][name]=[0].subStateId&agentStates[3][value]=1&agentStates[4][name]=[1].agentId&agentStates
[4][value]=15884&agentStates[5][name]=[1].projectId&agentStates[5][value]=999&agentStates[6][name]=[1].stateId&agentStates[6][value]=1&agentStates[7][name]=[1].subStateId&agentStates[7][value]=1
Which is all messed up...
You could use a JSON request:
$.ajax({
url: '#Url.Action("ChangeOutboundJob", "Blending")',
type: 'POST',
contentType: 'application/json; charset=utf-8',
data: JSON.stringify({
dialerJob: 'job',
agentStates: [
{ property1: 'value 1', property2: 'value 2' },
{ property1: 'value 3', property2: 'value 4' }
]
}),
success: function (result) {
// TODO: process the results
}
});
This will successfully map to the following controller action:
public void DoSomething(string dialerJob, MyViewModel[] agentStates)
where MyViewModel is defined like this:
public class MyViewModel
{
public string Property1 { get; set; }
public string Property2 { get; set; }
}
Remark: the JSON.stringify method is natively built into all modern browsers. If you need to support legacy browsers you need to include the json2.js script into your page.
change the js job var to be called dialerJob, the names must match for the mapper to auto map them.

Proper way to use AJAX Post in jquery to pass model from strongly typed MVC3 view

I'm a novice web programmer so please forgive me if some of my "jargon" is not correct.
I've got a project using ASP.NET using the MVC3 framework.
I am working on an admin view where the admin will modify a list of equipment. One of the functions is an "update" button that I want to use jquery to dynamically edit the entry on the webpage after sending a post to the MVC controller.
I presume this approach is "safe" in a single admin setting where there is minimal concern of the webpage getting out of sync with the database.
I've created a view that is strongly typed and was hoping to pass the model data to the MVC control using an AJAX post.
In the following post, I found something that is similar to what I am looking at doing:
JQuery Ajax and ASP.NET MVC3 causing null parameters
I will use the code sample from the above post.
Model:
public class AddressInfo
{
public string Address1 { get; set; }
public string Address2 { get; set; }
public string City { get; set; }
public string State { get; set; }
public string ZipCode { get; set; }
public string Country { get; set; }
}
Controller:
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
[HttpPost]
public ActionResult Check(AddressInfo addressInfo)
{
return Json(new { success = true });
}
}
script in View:
<script type="text/javascript">
var ai = {
Address1: "423 Judy Road",
Address2: "1001",
City: "New York",
State: "NY",
ZipCode: "10301",
Country: "USA"
};
$.ajax({
url: '/home/check',
type: 'POST',
data: JSON.stringify(ai),
contentType: 'application/json; charset=utf-8',
success: function (data.success) {
alert(data);
},
error: function () {
alert("error");
}
});
</script>
I have not had a chance to use the above yet. But I was wondering if this was the "best" method to pass the model data back to the MVC control using AJAX?
Should I be concerned about exposing the model information?
I found 3 ways to implement this:
C# class:
public class AddressInfo {
public string Address1 { get; set; }
public string Address2 { get; set; }
public string City { get; set; }
public string State { get; set; }
public string ZipCode { get; set; }
public string Country { get; set; }
}
Action:
[HttpPost]
public ActionResult Check(AddressInfo addressInfo)
{
return Json(new { success = true });
}
JavaScript you can do it three ways:
1) Query String:
$.ajax({
url: '/en/Home/Check',
data: $('#form').serialize(),
type: 'POST',
});
Data here is a string.
"Address1=blah&Address2=blah&City=blah&State=blah&ZipCode=blah&Country=blah"
2) Object Array:
$.ajax({
url: '/en/Home/Check',
data: $('#form').serializeArray(),
type: 'POST',
});
Data here is an array of key/value pairs :
=[{name: 'Address1', value: 'blah'}, {name: 'Address2', value: 'blah'}, {name: 'City', value: 'blah'}, {name: 'State', value: 'blah'}, {name: 'ZipCode', value: 'blah'}, {name: 'Country', value: 'blah'}]
3) JSON:
$.ajax({
url: '/en/Home/Check',
data: JSON.stringify({ addressInfo:{//missing brackets
Address1: $('#address1').val(),
Address2: $('#address2').val(),
City: $('#City').val(),
State: $('#State').val(),
ZipCode: $('#ZipCode').val()}}),
type: 'POST',
contentType: 'application/json; charset=utf-8'
});
Data here is a serialized JSON string. Note that the name has to match the parameter name in the server!!
='{"addressInfo":{"Address1":"blah","Address2":"blah","City":"blah","State":"blah", "ZipCode", "blah", "Country", "blah"}}'
You can skip the var declaration and the stringify. Otherwise, that will work just fine.
$.ajax({
url: '/home/check',
type: 'POST',
data: {
Address1: "423 Judy Road",
Address2: "1001",
City: "New York",
State: "NY",
ZipCode: "10301",
Country: "USA"
},
contentType: 'application/json; charset=utf-8',
success: function (data) {
alert(data.success);
},
error: function () {
alert("error");
}
});
This is the way it worked for me:
$.post("/Controller/Action", $("#form").serialize(), function(json) {
// handle response
}, "json");
[HttpPost]
public ActionResult TV(MyModel id)
{
return Json(new { success = true });
}
what you have is fine - however to save some typing, you can simply use for your data
data: $('#formId').serialize()
see http://www.ryancoughlin.com/2009/05/04/how-to-use-jquery-to-serialize-ajax-forms/ for details, the syntax is pretty basic.
If using MVC 5 read this solution!
I know the question specifically called for MVC 3, but I stumbled upon this page with MVC 5 and wanted to post a solution for anyone else in my situation. I tried the above solutions, but they did not work for me, the Action Filter was never reached and I couldn't figure out why. I am using version 5 in my project and ended up with the following action filter:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Mvc.Filters;
namespace SydHeller.Filters
{
public class ValidateJSONAntiForgeryHeader : FilterAttribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationContext filterContext)
{
string clientToken = filterContext.RequestContext.HttpContext.Request.Headers.Get(KEY_NAME);
if (clientToken == null)
{
throw new HttpAntiForgeryException(string.Format("Header does not contain {0}", KEY_NAME));
}
string serverToken = filterContext.HttpContext.Request.Cookies.Get(KEY_NAME).Value;
if (serverToken == null)
{
throw new HttpAntiForgeryException(string.Format("Cookies does not contain {0}", KEY_NAME));
}
System.Web.Helpers.AntiForgery.Validate(serverToken, clientToken);
}
private const string KEY_NAME = "__RequestVerificationToken";
}
}
-- Make note of the using System.Web.Mvc and using System.Web.Mvc.Filters, not the http libraries (I think that is one of the things that changed with MVC v5. --
Then just apply the filter [ValidateJSONAntiForgeryHeader] to your action (or controller) and it should get called correctly.
In my layout page right above </body> I have #AntiForgery.GetHtml();
Finally, in my Razor page, I do the ajax call as follows:
var formForgeryToken = $('input[name="__RequestVerificationToken"]').val();
$.ajax({
type: "POST",
url: serviceURL,
contentType: "application/json; charset=utf-8",
dataType: "json",
data: requestData,
headers: {
"__RequestVerificationToken": formForgeryToken
},
success: crimeDataSuccessFunc,
error: crimeDataErrorFunc
});

Resources