[NOTE:I am really looking for some good debugging techniques here. Perhaps some tricks or ways to simplify things of which I am unaware.]
I am using the technique of calling [WebMethods] defined in an ASPX page from JQuery as mentioned here and here. It seems to be an increasingly common method.
I've been using it for a while and, in general, it works great. But while developing it is pretty fragile. Any incorrect parameter will result in a really vague, non-specific, error message. For instance, if I have a fairly complex web method defined as:
[WebMethod]
public static string SaveComplexRecord(int recID, GeneralData general, SomeObject data, SomeOtherObject moreData)
{
//do a bunch of stuff with that data
}
And GeneralData, SomeObject, and SomeOtherObject all have a mix of various types of parameters (strings, ints, bools, datetimes.) It is very likely, especially during initial development, that I will build the JSON on the client side incorrectly. Perhaps I will do this:
var data = {
recID: curID,
general:
{
a: aValue,
b: bValue,
c: cValue
},
data:
{
d: dValue,
e: eValue,
f: fValue
},
moredata:
{
g: gValue,
h: hValue,
i: iValue
}
};
Which will result in an error because the name of the third parameter is moreData, not moredata. And that's just an example, there could be any of a hundred other subtle typo-style errors.
If I were calling this method from C# the compiler would give me an error message something like "No overloaded method of SaveComplexRecord takes three parameters." or some other helpful message that points you in the right direction.
So... is there a way of getting ASP.Net to produce better error messages here?
Or is there some utility that will automatically build the JSON parameter structure of a [WebMethod] call? (just like you can automatically get the WSDL of a web service)
...or any other technique that I may be missing?
And for completeness here is how I call these WebMethods from JQuery:
var jsondata = $.toJSON(data);
$.ajax({
type: "POST",
url: "MyWebPage.aspx/SaveComplexRecord",
data: jsondata,
contentType: "application/json; charset=utf-8",
dataType: "json",
beforeSend: function(xhr)
{
xhr.setRequestHeader("Content-type",
"application/json; charset=utf-8");
},
success: function(msg)
{
//do something on success
},
error: function(XMLHttpRequest, textStatus, errorThrown)
{
alert("ERROR status:" + textStatus + " error:" + errorThrown);
}
});
Or is there some utility that will automatically build the JSON parameter structure of a [WebMethod] call? (just like you can automatically get the WSDL of a web service)
Yes! The ASP.Net AJAX framework can do this! You could get the framework to generate client side proxy classes for GeneralData, SomeObject and SomeOtherObject classes using the 'GenerateScriptType' attribute on a web service class.
See understanding asp net ajax web servcies for a very good article about the subject.
[Unfortunately, AFAIAA, the GenerateScriptType has no effect when applied to the Page class where your page method is defined - so you will have to add an .asmx purely to get the proxy generation.]
You could perhaps use these classes to build up the data structure that you then JSON stringify when you call .ajax? [One of (the very few) things I really like about the MS AJAX framework is the client side proxy generation: it really does make calling web services and page methods very easy. Having said that, I too am moving towards using jQuery in preference to MS AJAX.]
Or alternatively...
Your problem is really that the de-serialisation of the JSON data into the arguments of your page method is done transparently by the framework (which in most cases is a good thing) but when it goes wrong, the feedback you get is less-than-helpful. If you want to trap de-serialisation problems then I think you have to take control of the serialisation either by using custom JSON converters (see here) or by using the rather inelegant sledgehammer approach of having your method accept a string and de serializing the JSN yourself in the method - which is trivial with anyone of the numerous JSON libs out there.
Javascript is dynamically typed so you can't get a compile-time error. But you could use the old window.onerror + ajax trick (or send the error via ajax in the error callback of jQuery.ajax()), and once you're in the server you can treat it just like any other runtime error (throw an exception, log the error, whatever)
From a jQuery standpoint, your problem is in the error function declaration. Only take one input parameter, and that will have all properties of the error, then you can debug more easily.
If the problem is server side, catch the error there, and create return json containing the error message.
Oh, and if you DO want to check your javascript at compile time, I recommend the add-in from jslint.com.
So:
$.ajax({
type: "POST",
url: "MyWebPage.aspx/SaveComplexRecord",
data: jsondata,
contentType: "application/json; charset=utf-8",
dataType: "json",
beforeSend: function(xhr)
{
xhr.setRequestHeader("Content-type",
"application/json; charset=utf-8");
},
success: function(msg)
{
//do something on success
},
error: function(err)
{
alert(e.message);
}
});
What I do when returning JSON from a web service is have an object called "ret" containing an attribute "err" as well as the attribute "data" containing the result of the service call. Inside the web service I trap any exceptions and put the exception message on the "err" attribute. Then in the client I check for the "err" attribute being non empty, if it is I know that an error occurred.
Related
I have the following API which returns json data as follows:
API : http://data.mtgox.com/api/1/BTCUSD/ticker
JSON : {"result":"success","return":{"high":.......
using jquery i tried the following but it is not giving me the data.
$.getJSON('http://data.mtgox.com/api/1/BTCUSD/ticker', function (data) {
alert(data);
});
and
$.ajax({
type: 'GET',
url: 'http://data.mtgox.com/api/1/BTCUSD/ticker',
dataType: 'json',
success: function (data) {
alert(data);
},
error: function (error) {
alert(error + "error");
}
});
but in first i get no alert
and in second i get error alert.
How can I read this data using jQUERY or C#?
THanks
As Archer mentioned this won't work if you're not on the same domain. There is one way around this using CORS (http://en.wikipedia.org/wiki/Cross-origin_resource_sharing) but you'd need to have control over the domain to set the required header or at least get the person in charge to do so.
The other option is to use JSONP which basically wraps the result in a function call that runs immediately when it returns by injecting a script tag. Problem is you lose nice things like error handling and you can't cancel the request.
I tried your problem. The error showed in Google Chrome Javascript Console was Origin http://localhost:1564 is not allowed by Access-Control-Allow-Origin
Check the answer to this Question and find your way out.
Is live data required ? Like "I must see the data as current as from this exact second" ?
If not (probably that is the case), I suggest you make a scheduled process (let's say every 5 min) on the web server. That will get data from the source (http://data.mtgox.com) and put it into database table.
After that you make your own JSON service (an MVC action method) and publish the data from your tables.
It will also allow your customers that your site is working even if mtgox.com is down for some reason.
How to getting Data through web Services with jquery in asp.net c#.
for more details
http://way2finder.blogspot.in/2013/08/how-to-getting-data-through-web.html
Does anyone know if it's possible to use jquery/ajax to call a c# (or any other .net) function in another project within the same solution?
Let's say that the solution's name is ExampleSolution , the name of the project from which I call the target function is Project.Source and the name of the target project is Project.Target,
and the name of the target function is TargetFunction().
I've tried following in an .js file in the Project.Source:
$.ajax({
url: '/ExampleSolution/Project.Target/TargetFunction',
type: 'get',
success: function (data) {
$(document.body).append(data);
}
});
Then I've modified the url-line in several ways but never succeed.
Do you have any advice?
Thank you all for your fast answers.
I found a solution for the problem and I'd like to share it just in case anybody faces the same problem in the future.
In the .js file before I call the $.ajax function I create a variable with help of window.location which points to the url to the targetfunction of the running target-project and use the variable in the ajax-function. So you don't point to another project. You point to url of running project.
Just as easy as it sounds.
Below follows the solution:
var url = window.location = 'http://localhost:13105/TargetFunction';
$.ajax({
url: url,
type: 'get',
success: function (data) {
$(document.body).append(data);
}
});
});
You can only call functions in the Code Behind because they're being registered by the web server.
If you want to have a function accessible outside the Code Behind it needs to be registered as a ASMX or WCF service.
See Creating and Consuming Your First WCF Service for setting up a WCF Service.
Once it is setup and running you can use Ajax to call the methods just like you would in the Code Behind.
$.ajax({
//Path to WCF Server and Target Method
url: "http://localhost:PORT/wcfsvc/FooService.svc/Foo",
type: 'get',
contentType: 'application/json; charset=utf-8',
success: function (data) {
$(document.body).append(data);
}
});
See also: Consuming WCF from jQuery as JSON
The short answer is "No, it isn't possible." Front-end code (like jQuery and AJAX) run on the client's machine, but C# functions are back-end and run on the server. There's no direct connection between them.
The longer answer is "Not directly, but there are ways to do something similar." Your easiest option is to use AJAX to POST to another controller/action on your server and then process the response. This is close to what you were doing, but you were slightly off. Rather than the url being a function, the url has to be an actual url on your website. Using /Functions/CallTargetFunction as an example, you would then create a controller like this:
public class FunctionsController : Controller
{
public ActionResult CallTargetFunction()
{
return Content(TargetFunction());
}
}
Note that doing this means anyone who visits http://yoursite.com/Functions/CallTargetFunction will get the result of that function.
Recently, I searched for a good engine to generate charts with Asp.Net Mvc 3. I finally found FusionChart which have a very nice varieties of chart types.
For helping me creating the xml required to display the chart, I found the project Libero. This project create a level of abstraction between the xml and the object model to define the properties of the graphic.
The project use Mvc2 and I tried to convert it to Mvc3. All the samples works perfectly except one; a sample with ajax call.
In the sample, the controller returns a ContentResult that returns a xml to update the graphic dynamically. The project works perfectly in Mvc2 but not in Mvc3.
Here is the code in the controller:
public ActionResult GetSalesXmlData(string period, string chartType, string chartTemplate)
{
var salesXmlData = this.Content(GetSalesChart(period, chartType, chartTemplate).ToXML(), "text/xml");
return salesXmlData;
}
And here is the code in the view:
$.ajax({
url: "/Home/GetSalesXmlData",
type: "POST",
data: { chartType: chartType, chartTemplate: chartTemplate, period: period },
dataType: "application/JSON",
success: function (data) {
Chart01.xmlData = data;
Chart01.chartType = chartType;
Chart01.showChart();
},
error: function () {
alert("XMLHttpRequest=" + XMLHttpRequest.responseText + "\ntextStatus=" + textStatus + "\nerrorThrown=" + errorThrown);
}
});
When I try to execute this code in Mvc3, I receive this error:
textStatus=parsererror
errorThrown=No conversion from xml to application/json
After searching for a while, I found how to correct my problem in this stackoverflow question.
After reading this post, I changed my controller code to this:
public JsonResult GetSalesXmlData(string period, string chartType, string chartTemplate)
{
var salesXmlData = this.Json(GetSalesChart(period, chartType, chartTemplate).ToXML(), "text/xml");
return salesXmlData;
}
And my ajax call to this:
$.ajax({
url: "/Home/GetSalesXmlData",
type: "POST",
data: { chartType: chartType, chartTemplate: chartTemplate, period: period },
dataType: "text json",
success: function (result) {
Chart01.xmlData = result;
Chart01.chartType = chartType;
Chart01.showChart();
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
alert("XMLHttpRequest=" + XMLHttpRequest.responseText + "\ntextStatus=" + textStatus + "\nerrorThrown=" + errorThrown);
}
});
My question is: Why after converting my project from Mvc2 to Mvc3, I must change the result returns by the controller from ContentResult to JsonResult and in my view, the dataType value from application/JSON to text json?
Thank you for your time.
In your original example, you were instructing jQuery to parse the response as JSON even though it was XML. If you upgraded from an older version of jQuery to a newer one as part of the MVC upgrade process, that would explain the parser error. Older versions of jQuery were fairly fast and loose about parsing AJAX responses, but later versions have begun using JSON.parse when jQuery believes the response to be JSON or you explicitly tell it to treat them as such.
Your newer version works around this by taking the XML and JSON-serializing that XML. While that technically does work, it has added an inefficient layer of redundant serialization.
Does your original approach work if you change the dataType to text/xml?
Speaking of efficiency, have you considered using JSON instead of XML? FusionCharts appears to support that. Then, you could avoid the XML altogether, use a more compact serialization over the wire, and take advantage of the browser's native JSON.parse method to more efficiently unpack the response too.
I have been playing with jstree (1.0rc2)+jquery (1.4.2) for the first time with c#.net and although I have gotten it working, there are a couple things that I don't understand about how data is provided to the tree by the webservice I use to populate the tree (using ajax and the json_data plug-in). I was hoping someone with more experience using jstree could provide some insight.
The jstree config looks like this:
"json_data": {
"ajax": {
"url": "GetTree.asmx/GetChildren",
"type": "POST",
"contentType": "application/json; charset=utf-8",
"dataType": "json",
"data": function(n) {
var result = "{'id':'" + (n.attr ? n.attr("id").replace("node_", "") : "0") + "'}";
return (result);
}
}
}
GetTree.asmx GetChildren method:
[WebMethod]
[ScriptMethod(ResponseFormat = ResponseFormat.Xml )]
public string GetChildren(string id)
{
List<jsTreeNode> jsTree = new List<jsTreeNode>();
//... (build the tree as needed)
JavaScriptSerializer serializer = new JavaScriptSerializer();
return(serializer.Serialize(jsTree));
}
Question 1: So everything works great, so what's the problem? The problem is "ResponseFormat = ResponseFormat.Xml". I struggled for a while to get this working because it did not work when it was set to ResponseFormat.Json, which is what I would expect it to be. In that situation, no errors would be reported by the web service or by jQuery when parsing the json response, but the tree would render empty.
In looking at the HTML output of the web service, I could see no difference between what was rendered either way. I was hoping someone could explain why this works (counterintuitively) and why it does not work with ResponseFormat.Json, and if this is indicative of something else I might be doing wrong.
Question 2: Generally, web service or web handler?
Would using a generic web handler (ashx) be a more efficient way to do this anyway? Is there a substantial difference in the overhead required of a standard web service versus a generic web handler? Since my goal is basically to control exactly what is output (and using the json data format in the web service doesn't seem to be working the way I want it to anyway) I am not sure what benefit, if any, there is to using a web service here instead of just stripping it down completely. On the other hand this works now so maybe I should leave well enough alone.
Seeing as this question has almost 600 views and no answers I thought I would answer it myself (since I've long since figured it out).
Using a ScriptMethod is really not the right way to communicate with jQuery ajax. While it can be done, you will notice what I was doing above is returning a string with data that I encoded myself to JSON using JavascriptSerializer.
However, using a ScriptMethod automatically incorporates serialization/deserialization designed to communicate with Microsoft's AJAX framework. Since serializing a pure string with no object wrapper will genererally result in the same string (whether I be returning XML or JSON format), it basically worked, but what was really happening internally is it was being serialized twice.
So what I should have done, at a minimum, was:
public List<jsTreeNode> GetChildren(string id)
that is, the return type should be the actual data type, not a string of serialized data.
However, this still wouldn't be exactly right, because Microsoft's methods wrap the return value in an object d. I could still extract that in Javascript to get the inner data. But if something like jsTree is expecting data in a predefined format this may not be workable.
The best solution is do not use WebServices, use generic handlers (ashx) instead. This gives you complete control over the format and handling of your input and output. It may take a little bit of doing to set yourself up a nice framework, but the frustration of being unable to skip parts of the WebService handling that you don't need makes it well worth it.
Sorry, I have to disagree with your answer the 3.5 framework has really good support for Json serialization and Web Services (and for 2.0 you can use Newtonsoft.Json). Please see my JsTree ASP.NET Web Control Demo at http://code.zyky.com/jsTreeView/Default.aspx and http://asp-net-elephant.blogspot.com/2012/01/how-to-use-jstree-in-aspnet-web-forms.html for an example of both. Hope this helps.
Regarding Question 1 (I cannot speak to Q2), I have gotten a web service to feed JSON back to the jsTree plugin. While I recognize that WCF and REST are more current approaches, and no doubt better in the long run, we still use asmx web services and they do get the job done. But it was not easy: I spent a while trying to get the exact syntax to work in JS so that jsTree would get its data object from an ASP.NET web service. As the OP alludes to in his solution, the problem isn't so much my JS and wiring of the plugin but was with the wonky JSON data that was returned my web service. It -looked- like JSON but it was a string representation of a simplified JSON object.
To fix it I had to deserialize and serialize my json string (I got a clue about this from https://stackoverflow.com/a/20080495/1129926) and the resulting object was consumed happily by jsTree. Here's my web service:
Imports System.Web.Script.Serialization
Public Function GetJsonData() As String
Dim jss As JavaScriptSerializer = New JavaScriptSerializer
' IMPORTANT: do not use single quotes ' in JSON string, use ""
Dim jsonData As String = "" & _
"{ ""text"": ""CONTACTS"", ""children"": [ { ""text"": ""CUSTOMER"", ""column"": ""CLASS"", ""children"": [ { ""text"": ""Excelo Communications"", ""column"": ""COMPANY"", ""children"": [{ ""text"": ""Fred Shorts"", ""column"": ""CONTACT"" }] } ] }, { ""text"": ""USER"", ""column"": ""CLASS"" } ] }"
Dim o As Object = Nothing
Try
' deserialize the JSON into an Object,
' shout out to: https://stackoverflow.com/a/20080495/1129926
o = jss.Deserialize(Of Object)(jsonData)
o = jss.Serialize(o)
Context.Response.Clear()
Context.Response.ContentType = "application/json; charset=utf-8"
Catch ex As Exception
// log something
End Try
Return o
End Function
On the client I initialized jsTree in script block as follows:
$(document).ready(function () {
var sURL = "../dataIO.asmx/GetJsonData";
var dataIn = "";
$.ajax({
async: true,
type: "POST",
url: sURL,
dataType: "json",
data: dataIn,
contentType: "application/json; charset=utf-8",
success: function (data) {
console.log("data obj:" + data);
createJSTrees(data);
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
console.log(XMLHttpRequest.statusText + "(status=" + XMLHttpRequest.status + "): " + XMLHttpRequest.responseText);
}
});
});
function createJSTrees(jsonData) {
$("#jstree_dataNav").jstree({
"core": {
"data": jsonData
}
});
$("#jstree_dataNav").on("changed.jstree", function (e, data) {
console.log(data.selected);
});
}
<div id="jstree_dataNav"></div>
jsTree has a somewhat alternative syntax whereas you call the web service within the core.data section but I was unable to get that to work. Instead, I call my web service via ajax and then pass the JSON data object to a function that initializes the jsTree plugin and jsTree just used the object passed in within data:.
I'm trying to use jQuery and JSON with a C# Web Service that I wrote. No matter what, the following code will only output in XML.
Webservice Code
[WebMethod]
[ScriptMethod(ResponseFormat = ResponseFormat.Json)]
public string HelloWorld() {
return "Hello World!";
}
I also have these attributes assigned to the class
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[ScriptService]
jQuery Code
$.ajax({
type: "POST",
contentType: "application/json; charset=utf-8",
url: "ScheduleComputerDS.asmx/HelloWorld",
data: "{}",
dataType: "jsonp",
success: function(data) {
alert(data);
}
});
The ASMX page always returns as content type "text/xml". Anything I'm missing?
EDITS: In response to a couple answers:
If I have the datatype as just "json" the content is still XML and jQuery also will not call my callback function. If I add the "&callback=?" to the url, IIS throws a HTTP 500 error.
My class does inherit from "System.Web.Services.WebService".
From doing some research on your guys answers, it looks like I do need to mess with WCF. Unfortunately the JSON that is returned is more designed for MS Ajax and is a lot of useless bloat for my use. I may look into an open source library like Jayrock or something similar.
Thanks for all your help!
I think there's a typo:
dataType: "jsonp",
Should be:
dataType: "json",
As far as I know, the ScriptService attribute just allows the service to automatically create a JavaScript proxy (by appending /js to the endpoint address - ScheduleComputerDS.asmx/js in your case). It does not allow you to call the operations on the service the way you're trying to do.
You could instead use a RESTful WCF service (which requires .NET 3.5) which you can access by sending a properly shaped URI via an HTTP GET.
Rich Strahl has a really basic post that should help you out with this.
http://www.west-wind.com/weblog/posts/164419.aspx
Have you tried with datatype json?
Also, have a look at Encosia's Using jQuery to Consume ASP.NET JSON Web Services article on the matter. There's some good info on common pitfalls too.