I'm using Ajax to make model and send it to the controller.
Here is Model:
public class PersonDto{
public Guid Id { get; set; }
public int PersonAge { get; set; }
public string PersonName { get; set; }
public DateTime? DateCreated { get; set; }
}
Here is Controller:
[Route("EditPerson")]
[HttpPost]
public async Task<IActionResult> EditPerson(PersonDto offer) {
//Save to Entity FW
}
Here is Ajax:
var data = {
Id: $('#personModal #personModalTitle').text(),
PersonAge: $('#personModal #personId').val(),
PersonName: $('#personModal #personName').val()
};
var dataJson = JSON.stringify(data);
console.log(dataJson);
$.ajax({
type: 'POST',
url: 'Persons/EditPerson',
data: dataJson,
contentType: "application/json",
success: function (result) {
},
error: function (error) {
Alert("Error Saving offer changes!");
}
});
Here is Console Log (Json), everything looks great:
{"Id":"96f2ae80-45cc-4a6c-abe0-230c2cbd3043","PersonAge":"5","PersonName":"John"}
When I Debug PersonsController I see that my model is never populated, is not null, just no data in it.
I tried Adding DateCreated to Model in Ajax function, I tried parsing Age to int.
I tried adding [FromBody] annotation to PersonDto in Action, then I getting NULL to my model.
I don't know where I can make error here.
For JSON encoded body , just add a [FromBody] :
[Route("EditPerson")]
[HttpPost]
public async Task<IActionResult> EditPerson([FromBody] PersonDto offer)
{
return new JsonResult(offer);
}
Update :
Another way to do that is use a [ApiController] to decorate the controller class .
If there's no [ApiController] , you should add a [FromBody] to enforce the Model Binder to read from body .
Whether a camelCased or pascalCased payload or not important . It makes no difference .
Camel Case :
POST https://localhost:44372/api/values/EditPerson HTTP/1.1
Content-Type : application/json
{"id":"96f2ae80-45cc-4a6c-abe0-230c2cbd3043","personAge":"5","personName":"John"}
Pascal Case :
POST https://localhost:44372/api/values/EditPerson HTTP/1.1
Content-Type : application/json
{"Id":"96f2ae80-45cc-4a6c-abe0-230c2cbd3043","PersonAge":"5","PersonName":"John"}
Can you try to get your data as a JObject:
[FromBody] JObject offer
[Route("EditPerson")]
[HttpPost]
public async Task<IActionResult> EditPerson([FromBody] JObject offer) {
string Id = (string)form.SelectToken("Id")
string PersonAge= (string)form.SelectToken("PersonAge")
string PersonName= (string)form.SelectToken("PersonName")
}
As I can see from your code you are not sending variable, the one you expect, "offer".
When doing AJAX call and sending data, data parameters should be named as those you are expecting.
You should send data in this format:
{
offer: { PersonAge: 10, PersonName: 'Senad' }
}
Your call should look like this
var data = {
Id: $('#personModal #personModalTitle').text(),
PersonAge: $('#personModal #personId').val(),
PersonName: $('#personModal #personName').val()
};
$.ajax({
type: 'POST',
url: 'Persons/EditPerson',
data: { offer: data }, //no need to serialize it
dataType: 'json',
success: function (result) {
},
error: function (error) {
Alert("Error Saving offer changes!");
}
});
I hope this is helpful.
Related
I am trying to post a javascript object, representative of a Model, back to a controller using an ajax post. However, the model is always showing as null.
The model in question is as follows
public class Product
{
[Key]
public int Id { get; set; }
[Required(ErrorMessage = "Name is required and must not be empty.")]
[StringLength(200, ErrorMessage = "Name should not exceed 200 characters.")]
public string Name { get; set; }
public DateTime Created { get; set; }
[Required(ErrorMessage = "Price is required and must not be empty.")]
[DataType(DataType.Currency)]
public decimal Price { get; set; }
}
With the ajax call looking like so
$('#btnSaveNewProduct').click(function (e) {
e.preventDefault();
var form = $('#frmNewProduct');
if (form.valid()) {
var data = { // to be replaced with form values
Name: 'Bob',
Price: 34
};
//ajax call to save product
$.ajax({
type: "POST",
url: "#Url.Action("AddProduct", "Admin")",
contentType: "application/json",
dataType: "json",
data: data,
success: function (response) {
alert('done');
},
error: function (response) {
alert(response);
}
});
}
});
The controller method looks like the following
[HttpPost]
public JsonResult AddProduct([FromBody] Product product)
{
bool success = false;
// save product
success = true;
return new JsonResult(success);
}
Any insight would be most appreciated.
To get your code to work as desired, make the following three modifications to your code.
From the Ajax call, remove this: dataType: "json"
From the Ajax call, remove this: data:data
In the Ajax call, add this: data:JSON.stringify(data)
IMPORTANT POINTS TO NOTE:
When you are using the [FromBody] attribute, the Content-Type value determines the formatter for the ASP.NET Core MVC Framework to use for parameter binding.
Since your Content-Type is application/json, a raw JSON string and not a JSON object, should be passed as data. Hence, apply JSON.stringify.
See this reference: Parameter Binding Using [FromBody] Attribute
I found below working:
$.ajax({
method: "POST",
data: { "Name": "bob", "Price": 10},
url: "#Url.Action("AddProduct", "Admin")",
success: function (data) {
//success login
},
error: function (data) {
alert('error' + data.status);
}
});
no need to mention dataType and contentType in ajax call. And your controller will like below:
[HttpPost]
public ActionResult AddProduct(Product product)
{
//You logic will be here
}
I post data using fetch like this in my client js scripts
fetch('/myarea/mycontroller/myaction', {
method: 'post',
body: JSON.stringify({ name: namevalue, address: addressvalue })
})
.then(function (response) {
if (response.status !== 200) {
console.log('fetch returned not ok' + response.status);
}
response.json().then(function (data) {
console.log('fetch returned ok');
console.log(data);
});
})
.catch(function (err) {
console.log(`error: ${err}`);
});
}, false);
Then on my controller
[HttpPost]
public async Task<IActionResult> MyAction(string name, string address)
{
// Error, name and address is null here, shouldn't be!
// more code here ...
}
My controller action is being called correctly, and I am able to debug inside it, but no data being passed. What could be wrong here? Thanks
The controller action is expecting query parameters (/myaction?name=myname&address=myaddress). That's the default.
You're sending them in the body.
You can change the javascript to send them as query parameters. (see here: https://github.com/github/fetch/issues/256)
Or you can tell the controller action to take the values from the body:
[HttpPost]
public async Task<IActionResult> MyAction([FromBody] Person person)
{
var myName = person.Name;
}
public class Person
{
public string Name {get; set; }
public string Address {get; set; }
}
The [FromBody] attribute kicked in for me only after I defined the header that it is an "application/json" type:
fetch('api/Profile/Update', {
method: 'post',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ description: "Some text here" })
});
And this is how the controller action looks like:
[HttpPost("[action]")]
public async Task<IActionResult> Update([FromBody] ProfileUpdateModel profileUpdateModel)
{
//Do some stuff here...
return RedirectToAction("/", "HomeController");
}
And the description property now receives the value from the request. Hope this gives final clarity to somebody tackling with the issue.
Or if you had <form>...</form> you could use
fetch('/url', {
method: 'POST',
credentials: 'include',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: $('#editContactModal form').serialize()
// body like "id=1&clientid=3&name=me&phones=123&email=#mail.com"
})
without [FromBody] !!! (just with regular action method in controller)
this is my model object
public class VentaProd
{
public IEnumerable<product> ListadoProductos {get; set;}
public IEnumerable<account> ListadoClientes { get; set; }
public sale Venta { get; set; }
}
If I use this code
$.ajax(
{
type: "Post",
dataType: "html",
url: '#Url.Action("buscarproducto", "detalle")',
data: {
vp: #Model.Venta.id
},
success: function (data) {
$('#divbuscar').html(data);
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
alert("algo salio mal" + errorThrown);
}
}
);
the before code works but my controller receive a null object, and if i change this code
data: {
vp: #Model.Venta.id
},
by
data: {
vp: #Model
},
the controller method is never called
unfortunately I could not use the first code because
my object VentaProd is not saved in database and this does not have a Id.
this is the controller method
public ActionResult buscarproducto(ejercicio1.Models.ClasesCombinadas.VentaProd vp=null)
{
return PartialView(vp);
}
If you only want to send back the ID the selection, you need to change the signature of the controller method to match the type of the id only:
public ActionResult buscarproducto(int? id=null)
If you want to have the model as a whole come back to the existing controller method you have, then you need to encode it:
vp: #Json.Encode(Model.Venta)
I am trying to send a complex Json with the following kind of form:
{
"Success":false,
"ErrorMessage":{
"ErrorMessage":"Some Message",
"ErrorType":"Serialization Failed",
"SiteReportDescription":"Some Desc",
"Status":false
},
"Result":null
}
To the following controller:
[Authorize, HttpPost]
public ActionResult DoThing(ThingModel model)
{
return Json(true);
}
Which accepts the following model (ThingError is another object with the four fields mentioned in the json):
public class ThingModel
{
public bool Success { get; set; }
public ThingError ErrorMessage { get; set; }
public ThingResult Result { get; set; }
}
I've used the following two methods to communicate with this Controller:
var form = $('<form method="post" enctype="multipart/form-data" style="visibility:hidden;"><input type="submit" name="model" /></form>');
$(document.body).append(form);
form.attr("action", '#Url.Action("DoThing", "Script")');
form.find("input[name='model']").val(JsonString);
form.submit();
$.ajax({
type: 'POST',
url: '#Url.Action("DoThing", "Script")',
contentType: "application/json; charset=utf-8",
processData: false,
dataType: 'json',
data: JsonString,
success: function (result) { }
},
});
Both of these methods of communication will interact with the controller but only the Ajax request will send a json string that is converted to the controller object ThingModel, the form.submit will just leave it null. What is the reason behind this? Is there something I am doing wrong in the Form?
You would need to capture this in fiddler in order to understand why.
Essentially, the ajax call is passing this:
{ "Something": "123" }
whereas the form is passing this:
{ "Model": { "Something": "123" } }
Not the same.
I have a class in my web project:
public class MyClass
{
public int? Param1 { get; set; }
public int? Param2 { get; set; }
}
which is a parameter in my controller method:
public ActionResult TheControllerMethod(MyClass myParam)
{
//etc.
}
If I call the method using POST, the model binding works automatically (I use angular on the js side, which likely doesn't matter):
$http({
method: "post",
url: controllerRoot + "TheControllerMethod",
data: {
myParam: myParam
}
}).success(function (data) {
callback(data);
}).error(function () {
alert("Error getting my stuff.");
});
If I use a GET, the parameter is always null in the controller.
$http({
method: "get",
url: controllerRoot + "TheControllerMethod",
params: {
myParam: myParam
}
}).success(function (data) {
callback(data);
}).error(function () {
alert("Error getting my stuff.");
});
Does complex model binding using the default model binder only work for POSTs, or is there something I can do to make this work with a GET?
The answer is Yes. The difference between GET and POST requests is that a POST body can have a content type so they can be interpreted correctly on the server side as XML, or Json, so on; for GET, all you have is just a querystring.
With ASP.NET MVC you can indeed bind your model on a GET request, as long as you have the same query string parameter names as of the property names of your Model class. Example from this answer:
public class ViewModel
{
public string Name { set;get;}
public string Loc{ set;get;}
}
You can do a Get request like this
MyAction?Name=jon&Loc=America
and MVC will automatically bind your model:
[HttpGet]
public ViewResult MyAction(ViewModel model)
{
// Do stuff
return View("ViewName", model);
}
Why are you calling the property "data" in the POST, and "params" in the GET? Both should be called "data".
$http({
method: "get",
url: controllerRoot + "TheControllerMethod",
data: {
myParam: myParam
}
}).success(function (data) {
callback(data);
}).error(function () {
alert("Error getting my stuff.");
});