I have a C# controller that should make an asyncronous call to another remote controller sending a simple object (model):
HttpClient clientHTTP = new HttpClient();
myModel model = new myModel();
model.Id = 100;
model.data = "this is data";
var json = Newtonsoft.Json.JsonConvert.SerializeObject(model);
clientHTTP.BaseAddress = new Uri("http://REMOTE_IP/");
clientHTTP.PostAsJsonAsync("/WebcastNotify/Heat", json)
.ContinueWith((postTask) => postTask.Result.EnsureSuccessStatusCode());
The remote controller:
public class WebcastNotifyController : Controller
{
public ActionResult Heat(myModel model)
{
// allways recieve model.Id = 0 and model.data = null
}
}
And the simple model:
public class modeloWebCast
{
public int Id { get; set; }
public string data { get; set; }
}
Why I getting allways and empty object at remote controller (model.id = 0, model.data=null)
Like peopple suggested, If i pass the model variable directly, I recieve null object (not object with null properties like before) in the controller.
I tried to use a simple html page to test:
function Send()
{
var Url = "#Url.Action("Heat", "WebcastNotify")";
$.ajax(
{
type: "post",
url: Url,
ajaxasync: true,
data: {modelo:{ Id: 100, data: "this is the data"}},
success: function (result) {
$("#CapaInfo").html(result);
}
})
}
And with this code the object is sent to the controller perfectly.
What can i doing wrong?
PostAsJsonAsync() takes an object to serialize as JSON.
You're passing it a string (which happens to be serialized JSON), so it serializes that to a JSON string ("...").
You need to pass it the actual object.
I believe the PostAsJsonAsync method accepts a normally typed value and does not expect it to have already been serialized.
See the MSDN page.
Try passing the model variable directly.
Related
Before I begin, I'd like to say - I realize that this question is very similar to many others that have been posted and answered on this site. I have read through and tried as many solutions as I could find that was related to my issue, and none have worked thus far.
I'm attempting to pass data from my web page to a controller method. The web page is very simple and only needs to capture information input by the user and send it off. I'm using Telerik's Kendo Grid to bind to and organize my data. No matter what I try, though, my AJAX post request never passes parameters forward correctly. When using my browser's debugger, I can see that the parameters being passed into the AJAX request are valid, but by the time they hit my breakpoint in the controller method, they are all either null or default.
Function Containing AJAX Request
function saveShiftDataToServer() {
var grid = $("#myGrid").data("kendoGrid");
var dataSource = grid.dataSource;
var allData = dataSource.data();
var comments = '#Model.Comments';
var loadInfoCorrect = '#Model.LoadInfoCorrect';
$.ajax({
type: "POST",
url: '/Home/SaveData',
data: JSON.stringify({ accessorials: allData, comments: comments, loadInfoCorrect: loadInfoCorrect }),
contentType: "application/json; charset=utf-8",
datatype: "json"
})
}
Controller Method
[AcceptVerbs("Post")]
public ActionResult SaveData(Accessorial[] accessorials, string comments, bool loadInfoCorrect)
{
// Code removed for brevity
}
My Kendo Grid is typed as Accessorial (the first controller method parameter type), so my assumption is that retrieving a collection of all present rows should return an array of that model. Even so, "comments" is a string, but is only ever passed to the controller method as null.
I'm new to ASP.NET Core and Kendo, so I'm sure there is something obvious that I'm missing. Any help would be appreciated!
I appreciate all of the responses! I was able to finally see valid data in my controller by changing the AJAX data type to "text" and simply passing the JSON directly for deserialization server-side. For some reason this is the only way that I've been able to make this work thus far.
AJAX POST Call
function saveShiftDataToServer() {
var grid = $("#accessorialGrid").data("kendoGrid");
var dataSource = grid.dataSource;
var allData = dataSource.data();
var shiftOverview = {
ShiftId: 0,
UserName: "test",
ShiftDate: null,
LoadList: null,
AccessorialList: allData,
LoadInfoCorrect: true,
Comments: ""
};
var jsonData = JSON.stringify(shiftOverview);
$.ajax({
type: "POST",
url: '/Home/SaveData',
data: { json: jsonData },
datatype: "text",
success: function (response) {
alert("111");
}
})
}
Controller Method
[AcceptVerbs("Post")]
public ActionResult SaveData(string json)
{
JsonConvert.DeserializeObject<ShiftOverview>(json); // This produces an object with valid data!
}
You could pass all your data in a ViewModel and get access to it using [FromBody] on action
public class ViewModel
{
public List<Accessorial> Accessorials{ get; set; }
public string Comments { get; set; }
public bool LoadInfoCorrect { get; set; }
}
Ajax:
var model = {};//pass all you data to an object
model.Accessorials = allData ;
model.comments = comments ;
model.loadInfoCorrect = loadInfoCorrect;
var items = JSON.stringify(model);
$.ajax({
url: '/GetAllCustDetails/SaveData',
type: "POST",
data: items,
contentType: 'application/json; charset=utf-8',
//dataType: "json",
success: function (response) {
alert("111");
}
});
Controller:
[HttpPost]
public ActionResult SaveData([FromBody]ViewModel model)
You are passing JSON object which corresponds to C# one like this:
public class Model {
public Accessorial[] Accessorials { get; set; }
public string Comments { get; set; }
public bool loadInfoCorrect { get; set; }
}
Try to declare such class above and adjust your action method this way:
public ActionResult SaveData(Model model)
{
// Code removed for brevity
}
If won't help - make model parameter object and check in debug mode what you are getting from AJAX call.
You juste have to use the [FromBody] attribute in your action method, like this :
[HttpPost]
public ActionResult SaveData([FromBody]Model model)
{
// Code removed for brevity
}
I'm having trouble with some JSON data populating a C# object in the parameter of a POST method on my Web API controller when I try to assign a property name in my JSON object.
Here is my API controller:
[HttpPost]
public HttpResponseMessage MyResource(MyComplexClass myClass)
{
var test = myClass.Name;
return Request.CreateResponse(HttpStatusCode.Created);
}
Here is the MyComplexClass:
public class MyComplexClass
{
public string Name { get; set; }
public string Id { get; set; }
}
And here is my AJAX call:
<script>
function test() {
var innerData = {
Name: 'Bob Loblaw',
Id: "1"
};
var myData = {
myClass: innerData
};
$.ajax({
type: "POST",
url: "api/Test/MyResource",
data: JSON.stringify(myData),
contentType: "application/json",
dataType: "json",
success: function (response) {
// do success stuff
},
error: function (err) {
// do error stuff
}
});
}
</script>
The problem is: when I include the innerData object as a property in the myData object in my JS code, the properties of the myClass object are all null.
I know that if I pass the innerData into the AJAX call itself, the myClass object is populated properly, but I'm curious about why I can't use a named JSON property to pass a complex C# object to the controller.
Does anyone know how to pass the myClass object as a named JSON property, or can you give an explanation as to why I can't?
Rephrased question:
If this JSON object:
var data = {
Name: 'Bob Loblaw',
Id: "1"
};
matches up with this controller method:
public HttpResponseMessage MyResource(string Name, string Id)
Why doesn't this JSON object:
var data = {
myClass: { Name: 'Bob Loblaw', Id: "1" }
};
match up with this controller method?
public HttpResponseMessage MyResource(MyComplexClass myClass)
Additional question:
Is it possible to have an API method declared as the following:
public HttpResponseMessage MyResource(MyComplexClass myClass, string anotherValue)
And if so, how would the JSON object be structured?
Why doesn't this JSON object:
var data = {
myClass: { Name: 'Bob Loblaw', Id: "1" }
};
match up with this controller method?
public HttpResponseMessage MyResource(MyComplexClass myClass)
Because of how model binding works in WEB API, when you send
var data = {
Name: 'Bob Loblaw',
Id: "1"
};
WEB API binds the properties of the json object you sent to the properties of the class you defined as a parameter.
So, in order to match
var data = {
myClass: { Name: 'Bob Loblaw', Id: "1" }
};
MyComplexClass would need to be defined as
public class MyComplexClass
{
public Object myClass{ get; set; }
}
It's because you've nested the innerData object and C# doesn't know what it is. You could either not nest that object by just passing the innerData to ajax or have another layer to your C# object like so:
public class MyOuterClass
{
public MyComplexClass innerData { get; set; }
}
I am designing a simple Web API service that takes the inputs on an html form, turns those inputs into json, and then sends that to a Web API. I am working with ASP.NET Web API in Visual Studio 2017. Here is some of my code to help me explain better:
This is my controller method that I am calling:
[HttpPost]
public AssessModel PostAssessment ([FromBody] AssessModel assess)
{
//Do something
return assess;
}
This is the model (simplified) that I'm using:
public class AssessModel
{
public Guid capitalassetassessmentid { get; set; }
public string ownerid { get; set; }
/*... Many more properties of int, bool, ect here ...*/
public string name { get; set; }
public string building { get; set; }
}
And finally this is the ajax call that I am using in my scripts:
$("form#my-form").submit(function (e) {
//First line here takes all fields and puts them in an array
var formArray = $(this).serializeArray();
//Second line takes that array and puts it into json format.
var jsonObj = JSON.stringify(formArray);
//Send request
$.ajax({
type: "POST",
url: "api/assessment/PostAssessment",
data: jsonObj,
contentType: "application/json",
dataType: "json",
success: function (jsonObj) {
$("#results").html(jsonObj.d);
}
});
});
So I am calling this ajax request when I submit a form. This ajax request should send my json as a string to the controller method but instead when I press submit I get an Error 405 - Method not Allowed. I am unsure why I am not allowed to send a POST verb to a controller method with the tag HttpPost.
It's also worth mentioning that my form tag is like this:
<form id="my-form" class="form" method="POST">
Any thoughts? Do I need to clarify anything? Let me know.
Try
var data = {};
var formArray = $('form#my-form').serializeArray();
for(var i in array)
{
data[formArray[i].name] = formArray[i].value;
}
var jsonObj = JSON.stringify(data);
I'm trying to convert a .NET object into a JSON string, because I want to be able to read the content of this object in the client side.
Here is my controller code:
public ActionResult Index()
{
IRightsManager rightsInfo = new RightsManager();
string userId = "ynz362897";
string json = JsonConvert.SerializeObject(rightsInfo.GetSectorsForUser(userId));
Session["test"] = json;
return View();
}
GetSectorsForUser returns an object which have only one attributes, a list of of another object. Here is the model:
public class Sector
{
public string Code { get; set; }
public string Name { get; set; }
public Sector(string code, string name)
{
this.Code = code;
this.Name = name;
}
}
public class RightsList
{
public IList<Sector> Sectors;
public RightsList(IList<Sector> sectors)
{
this.Sectors = sectors;
}
}
Here is GetSectorsForUser code:
public RightsList GetSectorsForUser(string userId)
{
IRightsManagerDB rightsManager = new RightsManagerDB();
RightsList rightsList = new RightsList(rightsManager.GetSectorsForUser(userId));
return(rightsList);
}
The result currently produced by my code is:
"{\"Sectors\":[{\"Code\":\"01\",\"Name\":\"FME\"},{\"Code\":\"02\",\"Name\":\"DML\"}]}"
Which is unreadable with a for in jQuery client side. I am stuck on this for hours, and I cant find any solutions.
Here is the client code:
var sectors = #Session["Sectors"];
$.each(sectors, function (i, item) {
$('#comboSector').append($('<option>', {
text: item.Name,
value : item.Code
}));
});
If you're sending the object through AJAX...
ASP.NET MVC handles JSON serialization for you. This means that you don't need the:
string json = JsonConvert.SerializeObject(rightsInfo.GetSectorsForUser(userId));
line. What happens is that you serialize the object yourself, and then ASP.NET MVC serializes it one more time, leading to the actual result, that is a string serialized as JSON. In other words:
The first serialization leads to {"Sectors": ...,
The serialization of the previous string leads to "{\"Sectors\": ....
If you're embedding the object in JavaScript within HTML...
It seems like this is what you are actually doing, and:
var sectors = #Session["Sectors"];
is a Razor file. This is a very weird approach (mixing languages, dynamically generating JavaScript, accessing the session from your view¹), but, well, let's assume you know what you are doing.
What happens here is that sectors variable points to a string which contains the JSON-serialized object, not the object itself. If you need to get the object, do:
var sectorsObj = JSON.parse(sectors);
$.each(sectorsObj, ...
JSON.parse decodes a JSON-serialized object. Similarly, JSON.stringify converts an object to its JSON representation.
¹ Accessing the session from your view like you do is not only an abuse of the MVC model and refactoring-unfriendly, but also technically wrong. Limit the view to the contents of the model, and eventually the view bag, when relevant. Avoid using global variables, session, request/response object, etc.
#MainMa Thanks for your answer. Like you said, it was not very clear technically for me. I did a bit of research and clean up my code according to standards. Now that I have a better understanding of Ajax, here is how I fixed my problem.
This is my ajax request client side.
$(document).ready(function () {
$.ajax({
url: '/Home/GetSectors',
type: 'GET',
dataType: 'json',
success: function (json) {
$.each(json, function (idx, sector) {
$('#comboSector').append($('<option>', {
text: sector.Name,
value: sector.Code
}));
})
},
error: function () {
}
});
})
Which is answered by my controller server side:
[HttpGet]
public JsonResult GetSectors()
{
Sector[] sectors = sectorManager.GetSectorsForUser("cn873284").ToArray();
return Json(sectors, JsonRequestBehavior.AllowGet);
}
Now my combo is initialized with parameter sent by the server. No more use of Session.
I have the following code in my jQuery file
var bases = {};
for (var j = 0; j < selectedVariants.length; j++) {
bases[selectedVariants[j].BId] = selectedVariants[j].CId;
}
and i am getting some data in bases dictionary now
and my question here is how do I pass this bases dictionary to controller through ajax call.
I tried the following thing but bases count in the controller is coming as ZERO
$.ajax({
url: $.url('~/TUP/Tflow'),
type: 'POST',
data: { baseDetails: JSON.stringify(bases)},
async: true,
cache: false,
});
Here when I see in my controller ... bases count is coming as ZERO
Please help me on this
Controller :
[HttpPost]
public JsonResult Tflow(JsonFileContentInputs basedetails)
{
//some code
}
and my model :
[ModelBinder(typeof(JsonModelBinder))]
[DataContract]
public class JsonFileContentInputs
{
[JsonProperty(PropertyName = "basedetails")]
[DataMember]
public Dictionary<string, string> basedetails { get; set; }
}
Try the following approach. As #EhsanSajjad mentioned, you'll need to call JSON.stringify on all of your data, not just the bases object:
$.ajax({
url: '/TUP/Tflow',
type: 'POST',
data: "json=" + JSON.stringify({baseDetails: bases}), // stringify everything,
dataType: 'text',
async: true,
cache: false
});
Then in your controller, instead of trying to use model binding, we can just deserialize the data ourselves using Json.NET.
Controller:
[HttpPost]
public ActionResult Tflow(string json)
{
// deserialize
var data = JsonConvert.DeserializeObject<JsonFileContentInputs>(json);
// more code
}
Model:
// You can drop these two as we aren't using the modelbinding
// [ModelBinder(typeof(JsonModelBinder))]
// [DataContract]
public class JsonFileContentInputs
{
[JsonProperty(PropertyName = "baseDetails")]
public Dictionary<string, string> BaseDetails { get; set; }
}
Unfortunately, reading the raw stream of the request in the controller seems to be necessary as MVC controllers won't play nice with raw JSON by default. More info here.
EDIT: It looks like you can pass raw JSON to an MVC controller, you just need to specify the ajax dataType as text and ensure the parameter names match up. I've updated my answer accordingly.
Instead of receiving the Class object, you should receive it as string and then serialize into object like below.
public JsonResult Tflow(string basedetails)
{
//some code
var model = new JavascriptSerializer().Deserialize<JsonFileContentInputs>(basedetails);
// Your code
}