Is there an ASP.NET Boilerplate Way of getting JSON data? - javascript

I am currently trying this, but I keep seeing the dreaded error:
Unexpected token o in JSON at position 1
I am struggling to find a solution and was wondering if anyone has any tips on how to solve this?
The class being serialized to JSON:
[Serializable]
public class GeoCoordinate
{
public GeoCoordinate()
{
}
[JsonProperty(PropertyName = "lat")]
public double Latitude { get; }
[JsonProperty(PropertyName = "long")]
public double Longitude { get; }
public GeoCoordinate(double latitude, double longitude)
{
Latitude = latitude;
Longitude = longitude;
}
public override string ToString()
{
return string.Format("{0},{1}", Latitude, Longitude);
}
}
Ajax call:
function getLocationData() {
jQuery.ajax({
url: abp.appPath + "Home/GetLocationsAsync",
type: "GET",
dataType: "json",
async: true,
success: function (data) {
var myArray = jQuery.parseJSON(data);
locations = [];
$.each(myArray, function (index, element) {
locations.push([element.lat, element.long]);
});
}
});
}
Controller:
[HttpGet]
public async Task<JsonResult> GetLocationsAsync()
{
var cords = await _practiceAppService.GetAllGeoCoordinates();
return Json(cords);
}
AppService:
public async Task<IList<GeoCoordinate>> GetAllGeoCoordinates()
{
var geoCoordinates = await Repository.GetAll()
.Where(x => !x.Disabled && !x.Latitude.Equals(0) && !x.Longitude.Equals(0))
.Select(x => new GeoCoordinate(x.Latitude, x.Longitude))
.ToListAsync();
return geoCoordinates;
}
Console.log of data before attempted call to parseJSON:
console.log(data);
var myArray = jQuery.parseJSON(data);

Coincidentally, there is a section called The ASP.NET Boilerplate Way that exactly addresses this.
Note that only point 2 is specific to ABP:
GetLocationsAsync returns a JSON object and not a string, so you should not call parseJSON.
You can reproduce the error message with: jQuery.parseJSON({});
ABP wraps JsonResult. From the documentation on AJAX Return Messages:
This return format is recognized and handled by the abp.ajax function.
You could use [DontWrapResult] attribute, but you might as well leverage on ABP here. abp.ajax handles the display of error messages if you throw a UserFriendlyException.
Since ajax is asynchronous, getLocationData cannot return locations directly.
You can return a chained Promise. If you're new to Promises, read Using Promises first.
There's a neater way than $.each and push.
You can use map.
Finally:
function getLocationData() {
return abp.ajax({
url: abp.appPath + "Home/GetLocationsAsync",
type: 'GET'
}).then(function (result) {
return result.map(function (element) {
return [element.lat, element.long];
});
});
}
Usage:
getLocationData().then(function (locations) {
console.log(locations);
});

ASP.NET Boilerplate wraps ASP.NET MVC action results by default if the return type is a JsonResult. So if you want to get the result you can disable wrapping. Try adding [DontWrapResult] attribute to the GetLocationsAsync method.
[DontWrapResult]
[HttpGet]
public async Task<JsonResult> GetLocationsAsync()
{
var cords = await _practiceAppService.GetAllGeoCoordinates();
return Json(cords);
}
PS: You don't need to add this attribute. You can get it from result field. Inspect the response via your browser's dev console to see how it's wrapped.

Related

Ajax success function not receive data

Below is webmethod of my web form which is returning a List of data and it works fine:
[WebMethod]
public static List<SalesInvoiceFinalCalculationEntity> salesInvoiceFinalCalculaiton(string InvoiceNo)
{
List<SalesInvoiceFinalCalculationEntity> list = new List<SalesInvoiceFinalCalculationEntity>();
list = SalesInvoiceManager1.salesInvoiceFinalCalculaiton(InvoiceNo);
return list;
}
But in below Ajax function, I can't retrieve the data. When I bind data to textbox in ajax success function, it displays Undefined text in Html textBox.
function salesInvoiceFinalCalculaiton() {
var InvoiceNo = $("#txt_InvoiceNo").val();
$.ajax({
async: false,
type: "POST",
contentType: "application/json; charset=utf-8",
url: "/AjaxRequestToServer.aspx/salesInvoiceFinalCalculaiton", //URI
data: "{InvoiceNo:'" + InvoiceNo + "'}",
dataType: "json",
success: function (data) {
document.getElementById("txtinvoicevalue").value=(data.totalprice);
document.getElementById("txtTotalDiscount").value = data.discountamt;
document.getElementById("txtTotalTaxableValue").value = data.taxableamt;
document.getElementById("txtTotalCGST").value = data.cgstamt;
document.getElementById("txtTotalSGST").value = data.sgstamt;
document.getElementById("txtGrandTotal").value = data.grandtotal;
},
error: function (xhr) {
if (xhr.statusText == "Invalid Request") {
sessionStorage.clear();
}
}
});
}
Here is Data Layer and the stored procedure:
public static List<SalesInvoiceFinalCalculationEntity> salesInvoiceFinalCalculaiton(string InvoiceNo)
{
try
{
List<SalesInvoiceFinalCalculationEntity> SalesInvoiceFinalCalculation = new List<SalesInvoiceFinalCalculationEntity>();
DataSet ds = SqlHelper.ExecuteDataset(Util.ConnectionString, CommandType.StoredProcedure, "sp_salesInvoiceFinalCalculaiton",
new SqlParameter("#InvoiceNo", InvoiceNo));
foreach (DataRow dr in ds.Tables[0].Rows)
{
SalesInvoiceFinalCalculationEntity list = new SalesInvoiceFinalCalculationEntity(dr);
SalesInvoiceFinalCalculation.Add(list);
}
return SalesInvoiceFinalCalculation;
}
catch (Exception ex)
{
throw ex;
}
}
And this is my entity Class:
public class SalesInvoiceFinalCalculationEntity
{
public int InvoiceNo { get; set; }
float totalprice { get; set; }
float discountamt { get; set; }
float taxableamt { get; set; }
float cgstamt { get; set; }
float sgstamt { get; set; }
float grandtotal { get; set; }
public SalesInvoiceFinalCalculationEntity() { }
public SalesInvoiceFinalCalculationEntity(DataRow dr)
{
InvoiceNo = Convert.ToInt32(dr["InvoiceNo"]);
totalprice = float.Parse(dr["totalprice"].ToString());
discountamt = float.Parse(dr["discountamt"].ToString());
taxableamt = float.Parse(dr["taxableamt"].ToString());
cgstamt = float.Parse(dr["cgstamt"].ToString());
sgstamt = float.Parse(dr["sgstamt"].ToString());
grandtotal = float.Parse(dr["grandtotal"].ToString());
}
}
why is data is not received in success function!
First of all, using async: false it is a bad practice because it's freeze your window during to your request. Don't use it.
The issue is that you have to return a json object from your server-side method in order to receive response in success callback function of your ajax method.
[WebMethod]
public static string salesInvoiceFinalCalculaiton(string InvoiceNo)
{
List<SalesInvoiceFinalCalculationEntity> list = new List<SalesInvoiceFinalCalculationEntity>();
list = SalesInvoiceManager1.salesInvoiceFinalCalculaiton(InvoiceNo);
return JsonConvert.SerializeObject(list);
}
Web requests work with json format.
Finally resolved it. I forgot to mentioned
Public
in
SalesInvoiceFinalCalculationEntity
entity all variables and document.getElementById("txtinvoicevalue").value=(data.d[0].totalprice); this should be instead of
document.getElementById("txtinvoicevalue").value=(data.totalprice);
I think the problem is, that you're trying to send a Java Class to your JavaScript file. You can only send simple data types numbers, letters. As I understand, you're trying to access the member variables of SalesInvoiceFinalCalculationEntity.
If that's the case, you need to send it as a JSON object and get the data like this and then parse it.
The idea behind AJAX is to make the experience for the user better by not freezing the website using asynchronous requests. By calling the AJAX call with
async: false
removes the idea behind AJAX. You could then simply make a normal call to the server.
Use this:
https://www.newtonsoft.com/json
Serialize your list and return it as a string.
Then, in your javascript:
success: function (data) {
data = JSON.parse(data);
document.getElementById("txtinvoicevalue").value=(data.totalprice);
document.getElementById("txtTotalDiscount").value = data.discountamt;
document.getElementById("txtTotalTaxableValue").value = data.taxableamt;
document.getElementById("txtTotalCGST").value = data.cgstamt;
document.getElementById("txtTotalSGST").value = data.sgstamt;
document.getElementById("txtGrandTotal").value = data.grandtotal;
},
Try
success: function (data.d) rather than success: function (data). If I recall when using webmethods the return object is within data.d and not simply data.
Also, despite what other answers say. When using the [webmethod] attribute and jquery ajax, you do not have to convert your response object to json. It will do so automatically.

Cannot post complex object

I have the function on my view:
function setMessengerState() {
var serviceURL = $("#messenger-set-state-url").val();
var data = {
messengerState: g_messengerState,
};
$.ajax({
type: "POST",
url: serviceURL,
data: JSON.stringify(data),
contentType: "application/json; charset=utf-8",
dataType: "json",
success: successFunc,
error: errorFunc
});
function successFunc(data, status) {
console.log("saved");
}
function errorFunc(data, status) {
console.log("failed?");
}
}
This is how the JSON.stringify(data) is formated on Chrome debugger:
"{"messengerState":{"IsOpen":true,"ConversationStates":[{"PartnerId":"64c71990-9ddc-4967-8821-a8e5936560a3","IsEnabled":true},{"PartnerId":"64c71990-9ddc-4967-8821-a8e5936560a3","IsEnabled":true}]}}"
And this is the value of $("#messenger-set-state-url").val():
"/Messenger/Messenger/SetMessengerState"
The controller method:
[HttpPost]
public async Task<ActionResult> SetMessengerState(MessengerStateInfo messengerState)
{
var user = User.ApplicationUser();
if (user == null)
return null;
bool success = await MvcApplication.Messenger.SetState(user, messengerState) != null;
return Json(success, JsonRequestBehavior.DenyGet);
}
And, finally, this is MessengerStateInfo and ConversationStateInfo:
public class MessengerStateInfo
{
public bool IsOpen { get; set; }
public ICollection<ConversationStateInfo> ConversationStates { get; set; }
public MessengerStateInfo()
{
ConversationStates = new ConversationStateInfo[0];
}
}
public class ConversationStateInfo
{
public string PartnerId { get; set; }
public bool IsEnabled { get; set; }
}
I cannot find where I'm doing it wrong. The post never gets to the controller method. I tried before with basic (string) parameters and it works just fine, but it simply doesn't get through with complex objects.
Thank you for writing a detailed question that allows reproducing the problem.
The model binder expects your ICollection to be writable, but arrays are not writable in this manner. Do a simple experiment:
ICollection<int> a = new int[0];
a.Clear();
A "Collection is read-only" exception will be thrown.
Now, how do you fix this. Change your MessengerStateInfo class definition to the following:
public class MessengerStateInfo
{
public bool IsOpen { get; set; }
public ICollection<ConversationStateInfo> ConversationStates { get; set; }
}
Here we removed the constructor, which allows the model binder to create a new instance of List<> type. This one, of course will be read-write and the binding succeeds.
Here are relevant code snippets from the model binder source code. This one is from System.Web.ModelBinding.CollectionModelBinder<TElement> class:
protected virtual bool CreateOrReplaceCollection(ModelBindingExecutionContext modelBindingExecutionContext, ModelBindingContext bindingContext, IList<TElement> newCollection)
{
CollectionModelBinderUtil.CreateOrReplaceCollection<TElement>(bindingContext, newCollection, () => new List<TElement>());
return true;
}
And this one is one is from System.Web.ModelBinding.CollectionModelBinderUtil:
public static void CreateOrReplaceCollection<TElement>(ModelBindingContext bindingContext, IEnumerable<TElement> incomingElements, Func<ICollection<TElement>> creator)
{
ICollection<TElement> model = bindingContext.Model as ICollection<TElement>;
if ((model == null) || model.IsReadOnly)
{
model = creator();
bindingContext.Model = model;
}
model.Clear();
foreach (TElement local in incomingElements)
{
model.Add(local);
}
}
As you can clearly see from this code, if the collection not empty Clear method is called on it, which, in your case leads to an exception. If, on the other hand the collection is null, new List<> is executed which results in a brand new (writable) object.
Note: implementation details may differ depending on software version. The code above may differ from the actual code in the version of the library that you are using. The principle remains the same though.
Here is a tip how to find the reason faster than typing the question to Stackoverflow.
Put a breakpoint on public async Task<ActionResult> SetMessengerState(MessengerStateInfo messengerState) and observe that the breakpoint is not hit. Open chrome console and take notion of the error icon.
Click on the icon to see the error message at the bottom.
Now click on then link in the error message, you'll see this screen:
Finally click on the request in the "Name" column. You will see this:
This gets you the actual error message with the stack trace. In most cases you will be able to tell what's wrong from this message. In this particular case you will immediately see that an array is tried to be cleared and fails.
The error is actually modifying ICollection<ConversationStateInfo> object in ConversationStates = new ConversationStateInfo[0];
Changing it to List should solve the issue.
Set $.ajaxSettings.traditional to true. I already have the same problem, and works for me
$.ajaxSettings.traditional = true;
$('#btn').click(function () {
var array = [];
var url = '/Controller/Action';
$.post(url, { array : array });
});
});

making .ajax request to mvc controller - but .done function never executes

So basically Here is what I do:
in body - onload method I call this javascript function
function TestN() {
var list = new Array();
var allElements = document.getElementsByTagName('*');
$("*[wordNum]").each(function ()
{
var endRes = {
ControllerName: this.id,
WordNumber: this.getAttribute("wordNum")
};
list.push(endRes);
});
jQuery.ajax({
url:' #Url.Action("Translate")' ,
contentType: "application/json",
dataType: "json",
data: { List : JSON.stringify(list) }
,
traditional: true
})
}
what it does - it searches all the controlls with attribute "WrdNum" and then I make an ajax request to the MVC Translate action!
In the Translate Action I make a request to a web service which populates a list of type - TranslateModel
public ActionResult Translate(string List)
{
List<TranslateModel>listto = WebServiceBea.TranslateList(1, List);
return View(listto);
}
Also Here is my TranslateModel
public class TranslateModel
{
public string ControllerName { get; set; }
public string WordNumber { get; set; }
public string Description { get; set; }
}
So basically my question is -> what type should I return to a view - > and how to return this list to a javascript or jquery function which has to set the innerHtml property of some html controls with the record from this list.**
I now that it's strange but that's my task
EDIT
Thanks so much for the help. But now I've got another problem:
After I changed my javascript and put. Done method so I could get the data from the server it looks something like this :
$(document).ready(function () {
var list = new Array();
$("*[wordNum]").each(function () {
var endRes = {
ControllerName: this.id,
WordNumber: this.getAttribute("wordNum")
};
list.push(endRes);
});
jQuery.ajax({
url: ' #Url.Action("Translate")',
contentType: "application/json",
dataType: "json",
data: { List: JSON.stringify(list) }
,
traditional: true,
}).done(function (result)
{
alert ("HII") ;
});
});
no matter what I put in the .done function it never executes. It seems like the controller doesn't know where to return the result. |I| don't now. Can something happen from the fact that I'm making this request from the .layout page - on document ready. s
this looks like a greet place to use knockout js.
here is a great step by step for using knockout with the mvc view
so the method will only return json, the view will not have a model just a call to get the json
if you are going to use $.post to pull your data you could return your list as json
[AcceptVerbs(HttpVerbs.Get|HttpVerbs.Post)]
public ActionResult Translate(string List)
{
List<TranslateModel>listto = WebServiceBea.TranslateList(1, modelObj);
return Json(listto);
}
Looking at what you are posting to the action method, it should already be a list of that type. MVC should do the heavy lifting and transform it to the objects you have.
if however you would like to handle the return yourself you can do something like
jQuery.ajax({
url:' #Url.Action("Translate")' ,
contentType: "application/json",
dataType: "json",
data: { List : JSON.stringify(list) },
traditional: true
}).success(function( returnData, returnStatus)
{
//some code to handle the list of objects reutrned
});
You've already got an answer, but consider the following for cases where you may have controller actions called by javascript:
public ActionResult GetItems(string id)
{
var MyList = db.GetItems(id);//returns a list of items
if (Request.IsAjaxRequest())//called from javascript via AJAX
{
return Json(MyList, JsonRequestBehavior.AllowGet);
}
else //regular hyperlink click
{
return View(MyList);
}
}
To use the list, do the following
$.ajax({url: "'#Url.Content("~/controllername/GetItems")?id=' + id"})
.done(function(result){
var mylist = result.responseText.evalJSON();//this is your list of items
for(i=0;i<mylist .length;i++)
{
var myitem = mylist[i];
}
});
NEVERRRR NEVERRR Forge to put jsonRequestBehavior.AllowGet
Thanks alot for everyone for the help

How to populate javascript variable with JSON from ViewBag?

I have this Index action:
public ActionResult Index()
{
var repo = (YammerClient) TempData["Repo"];
var msgCol = repo.GetMessages();
ViewBag.User = repo.GetUserInfo();
return View(msgCol.messages);
}
GetMessages returns a list of POCO messages and GetUserInfo returns a POCO with the info of the user (id, name, etc).
I want to fill a javascript variable with the JSON representation of the user info.
So I would want to do something like this in the view:
...
<script>
var userInfo = "#ViewBag.User.ToJson()"
</script>
...
I know that doesn't work, but is there a way to do that? I want to avoid having to do an ajax request once the page is loaded just to get the user info.
In View you can do something like this
#{
var jss = new System.Web.Script.Serialization.JavaScriptSerializer();
var userInfoJson = jss.Serialize(ViewBag.User);
}
in javascript you can use it as
<script>
//use Json.parse to convert string to Json
var userInfo = JSON.parse('#Html.Raw(userInfoJson)');
</script>
Was using this solution for simple objects. But I had some problems getting an array to js objects so I'll just leave what I did here.
C#
#{
using Newtonsoft.Json;
ViewBag.AvailableToday = JsonConvert.SerializeObject(list);
}
js
var availableToday = JSON.parse('#Html.Raw(ViewBag.AvailableToday)');
Client-Side Code:
This is an ajax call to a .Net MVC Controller:
var clientStuff;
$.ajax({
type: 'GET',
url: '#Url.Action("GetStuff", "ControllerName")',
data: {},
dataType: "json",
cache: false,
async: false,
success: function (data) {
clientStuff = data;
},
error: function(errorMsg) {
alert(errorMsg);
}
});
Server-Side Code:
CONTROLLER:
public JsonResult GetStuff()
{
return Json(_manager.GetStuff(), JsonRequestBehavior.AllowGet);
}
MANAGER:
public IEnumerable<StuffViewModel> GetStuff()
{
return _unitofWork.GetStuff();
}
UNIT OF WORK:
public IEnumerable<StuffViewModel> GetStuff()
{
var ds = context.Database.SqlQuery<StuffViewModel>("[dbo].[GetStuff]");
return ds;
}
Unit of Work can be a query to a sproc (as I have done), a repository context, linq, etc.
I'm just calling a sproc here for simplicity, although it could be argued that the simplicity lies with Entity Framework and Linq.
You can change this line :
ViewBag.User = repo.GetUserInfo();
To
ViewBag.User = new HtmlString(repo.GetUserInfo());
You should add using Microsoft.AspNetCore.Html; or using System.Web; if HtmlString is not accessible.

Javascript Deserialize is returning the class name instead of actual object

So I'm running GetServerUpdateProgress() in the controller from a $.ajax call on my page. While debugging I can confirm that the variable: myobj is being properly created and filled with the correct data.
But when on the $.ajax success, I'm not getting the data in json format, instead I'm getting
a string of "TrackerMVC.ClassLib.UpdateAJAXProgress" - the objects type.
I've done this in the past with a .svc webservice and didn't have any problems getting the object values using this exact same method.
Any ideas? Thanks!
method:
public UpdateAJAXProgress GetServerUpdateProgress()
{
string BASE_URL = "http://localhost:55094";
string url = BASE_URL + "/Home/UpdateProgress";
WebRequest wr = WebRequest.Create(url);
wr.Credentials = CredentialCache.DefaultNetworkCredentials; // uses current windows user
var myojb = new UpdateAJAXProgress();
var response = (HttpWebResponse)wr.GetResponse();
var reader = new StreamReader(response.GetResponseStream());
JavaScriptSerializer js = new JavaScriptSerializer();
var objText = reader.ReadToEnd();
myojb = (UpdateAJAXProgress)js.Deserialize(objText, typeof(UpdateAJAXProgress));
return myojb; // during debugging this object has the correct values in the correct format
}
class:
public class UpdateAJAXProgress
{
public int Completed { get; set; }
public int Total { get; set; }
}
javascript:
$.ajax({
type: "POST",
async: false,
url: '#(Url.Action("GetServerUpdateProgress","Charts"))',
contentType: "application/json; charset=utf-8",
success: function (data) {
console.log(data); // data being returned is: "TrackerMVC.ClassLib.UpdateAJAXProgress"
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
alert(XMLHttpRequest.status);
alert(XMLHttpRequest.responseText);
}
});
You're misusing MVC.
You should declare your function as returning ActionResult, then return Json(myobj).
If you return a non-ActionResult from an MVC action, MVC will convert it to a string by calling ToString().

Categories

Resources