Getting a complex object through jQuery/ajax in ASP.NET - c#

Here's my situation: I have a UserBadge object in ASP.NET, it contains 3 fields, being a User object, a Badge object and a boolean (isNotified) to check if the user has been notified of earning a badge. I'm having issues sending a specific UserBadge from this WebMethod():
[WebMethod()]
public static UserBadge Notify()
{
var db = new achievDb();
foreach (var uB in db.UserBadges)
{
if (System.Web.HttpContext.Current.User.Identity.Name == uB.User.UserName)
{
if (!uB.isNotified)
{
return uB;
}
}
}
return null;
}
to my $.ajax:
<script type="text/javascript">
$(document).ready(function () {
$.ajax({
type: "POST",
url: "../NotifCodeBehind.aspx/Notify",
data: "{}",
complete: function (result) {
if (result) {
$("#notify").jGrowl("You've unlocked a badge!", { header: 'Yay', close: function () {
$.ajax({
type: "POST",
url: "../NotifCodeBehind.aspx/Notified",
data: "{}",
success: function (ub) { DoCallback(JSON.stringify(ub)); },
error: function () { DoCallback("NOPE!") }
});
}
})
};
function DoCallback(msg) {
alert(msg);
}
}
})
})
</script>
and then back to another WebMethod() that sets the isNotified boolean to true once the notification is closed:
[WebMethod()]
public static void Notified(UserBadge ub)
{
var db = new achievDb();
foreach (var userbadge in db.UserBadges)
{
if (userbadge.UserId == ub.UserId && userbadge.BadgeId == ub.UserId)
{
userbadge.isNotified = true;
db.SaveChanges();
}
}
}
The Problem:
I have absolutely no idea how to actually pass the object to the ajax, and then back again... I've spent about 1,5 days browsing the internet about it, but now, I've decided to come for help. The more I read about it, the more it confuses me, and I'm an absolute newbie to jQuery/Ajax/JSON.
So if you could keep it as simple as possible, and nudge me in the right direction, it would be most appreciated!
EDIT:
New JavaScript below, thought I had it, but I didn't.
EDIT2:
This is now solved, I ended up using a controller instead of WebMethods.

You want to work with JSON serialization. When you return the result to your ajax callback method, your web method can return result in form of XML, JSON or string. If you return a JSON, your complex object will be converted to a json object in a very straight forward manner.
Assuming your class structure
class UserBadge
{
User UserProperty { get; set; }
Badge BadgeProperty { get; set; }
bool IsNotified { get; set; }
}
class User
{
string Username { get; set; }
}
Your json object in javascript from the result callback function will look like
{
UserProperty: { Username: "some username" },
BadgeProperty: { /**********/ },
IsNotified: true
}
As you can see, your JSON structure is the same as your class object structure.
So, calling result.UserProperty.Username in javascript is perfectly ok.
Constructing the same object and passing it to another ajax web service will transform the JSON object to the managed class objects.
Edit: You can add the ScriptMethodAttribute to your WebMethod to specify JSON response.
[WebMethod]
[ScriptMethod(ResponseFormat = ResponseFormat.Json)]
public static UserBadge Notify()
{
}

Do you really want to pass the object to your web method ? Why not pass the ids ( UserId, badgeId etc ) and Build the object in your ajax server page using those Id's if needed. You can pass the Id as the query string values.
var userId=4 // read from some hidden items or somewhere
var badgeid=3 // read from somewhere
$.ajax({
type: "POST",
url: "../NotifCodeBehind.aspx/Notify?uid="+userId+"&bid="+badgeId,
data: "{}",
complete: function (result) {
// rest of the code
EDIT : From the Comment, its clear that it is an ASP.NET MVC App
Since it is an ASP.NET MVC application, You can do model binding. You can serialize your form and send that to the controller action via jquery post.
If your page is having a "LogOnMOdel" and you want to do this binding for the UserBadge object as well, you need to create another ViewModel which has 2 properties , one is LogonModel and another is UserBadge. then pass that view model to your page.

Well, finally figured it out with a little help from my brother and some good old fashioned exercise! I solved the issue by using a controller instead of a code-behind with WebMethods.

Related

AJAX POST Request Not Passing Data to Controller Method in ASP.NET Core 2.2

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
}

'Error 405 - Method not Allowed' when sending Ajax POST request in ASP.NET Web API

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);

Invalid output produced using JSON serialization

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.

AJAX Post Multiple Data to ASP.Net MVC

I am facing problem while posting multiple objects via ajax jquery to MVC 4 controller. It has been weeks but I can't seem to find a solution.
I tried several approaches, sometimes the filterModel object is null and sometimes string parameters are null (doesn't matter even if I stringify of if I specify contentType or not)
What I want?
I want to pass three objects: 1. filterModel 2.testparamA 3.testparamB
What should I do to pass all three objects to MVC controller? What do I need to write in data: so I get all 3 object values?
The simplest Controller
[HttpPost]
public JsonResult Test(string testparamA, string testparamB, FilterModel filter)
{
using (RBSystemEntities repository = new RBSystemEntities())
{
return Json(new {
DataList = repository.Items.Select(x => new {x.PKID, x.ItemName}).ToList(),
Result = "OK"
});
}
}
The simplest View
var filterModel = #Html.Raw(Json.Encode(new FilterModel("ItemName", "Pepperoni Pizza")))
//filterModel = JSON.stringify(filterModel);
function testme() {
// post the javascript variable back to the controller
$.ajax({
url: '/Menu/Test',
type: 'POST',
//contentType: 'application/json; charset=utf-8',
data: {
filter: filterModel,
testparamA: 'A value',
testparamB: 'B value'
}, // with this approach I get filterModel null in the controller however testparamA and testparamB has values
data: filterModel, // with this approach I get values for filterModel but I can't pass testparamA and testparamB
success: function (result) {
// TODO: do something with the results
alert('success');
}
});
}
testme();
The simplest FilterModel class
public class FilterModel
{
public FilterModel() { }
public FilterModel(string filtercolumn, string filtervalue)
{
this.FilterColumn = filtercolumn;
this.FilterValue = filtervalue;
this.FilterColumnCriteria = "=";
}
public string FilterColumn { get; set; }
public string FilterValue { get; set; }
public string FilterColumnCriteria { get; set; }
}
Hope you don't mind me posting my comment (which was helpful) as an answer...
If you use stringify as follows it should work...
JSON.stringify({ fm: filterModel, ta: testparamA, tb: testparamA })
#PaulZahra guided me to the right direction.
Below changes fixed my problem:
contentType: 'application/json; charset=utf-8', // uncomment this
data: JSON.stringify({ filter: filterModel, testparamA: 'A value', testparamB: 'B value' }), //stringify whole thing and not just c# class

Map Json to Dictionary

I am trying to map Json to Dictionary. Earlier I had Dictionary with key of type 'long' . But after realizing for mapping to happen key needs to be of type 'string or 'object'.
Now my type definition in c# is like this :
public class StyleItemCreateCommand
{
public long StyleId { get; set; }
public Dictionary<String, string> SelectedItemToColorMap { get; set; }
}
And json which I am generating is like this:
{"StyleId":"1710","SelectedItemToColorMap":{"1391":"583","21531":"7733"}}
But still somehow it is not being mapped. I am using asp.net mvc controllers as a service, which is being consumed by jquery client.
MVC method signature is as following:
[HttpPost]
public ActionResult Create(StyleItemCreateCommand command)
{
}
The dictionary object is always null. Any help is appreciated. Thanks.
Now after searching around web I found out that ASP.Net MVC is not gonna do it implicitly. Found this answer :
[https://stackoverflow.com/a/15220050/756722]
I think the reason why this is happening is that
"SelectedItemToColorMap":{"1391":"583","21531":"7733"}
is an object and your StyleItemCreateCommand
class defines a Dictionary. Change the class property to:
public object SelectedItemToColorMap { get; set; }
and you should be able to see the values. You can then redesign your class/es.
Alternatively amend the json by surrounding the { } with [ ] converts SelectedItemToColorMap to an array of key value items.
UPDATE
Just tried in asp.net mvc 4 with a simple view
<input type="button" value="click1" id="click1" />
#Scripts.Render("~/bundles/jquery")
<script>
$(function () {
//this is called by a Get action on the controller
$('#click1').click(function (e) {
var jsonObject = { "StyleId": "1710", "SelectedItemToColorMap": { "1391": "583", "21531": "7733" } };
$.ajax({
url: "#Url.Action("Create")",
type: "POST",
data: JSON.stringify(jsonObject),
contentType: "application/json; charset=utf-8",
dataType: "json",
error: function (response) {
//process error;
},
success: function (response) {
//process success;
}
});
});
});
</script>
The above appears in the body of the view. And the controller is
[HttpPost]
public ActionResult Create(StyleItemCreateCommand command)
{
if (command != null) {
string value1 = command.SelectedItemToColorMap["1391"];
string value2 = command.SelectedItemToColorMap["21531"];
Debug.Assert(value1 == "583" && value2 == "7733");
}
return View();
}
Which uses your StyleItemCreateCommand - and it all works. Ok the above uses JSON.stringify(jsonObject) so what format is your json object that is actually coming from the post request? It would be interesting to see the request body (eg using chrome developer tools under the network section).
.Net serialization (not being used here) will wrap requests in a .d object as a security measure against auto execution of code, could this be what is happening?

Categories

Resources