jQuery / ASP MVC -- parsererror in "$.ajax" calls - asp.net

I'd like to create a simple action link in ASP.Net MVC RC2 using jQuery 1.3.1 - something like this:
Tester
with the AjaxTest1 function:
function AjaxTest1() {
$.ajax({
url: "Home/Ajax1",
error: function(request, status, error) {
alert("error: " + status + ", " + "\n" +
error + ", " + request.responseText +
request.getAllResponseHeaders());
},
success: function(data, status) {
alert("Finally, it worked!");
},
type: "GET",
dataType: "text",
});
return false;
}
and controller action:
public ActionResult Ajax1()
{
return this.Content("Test Content");
}
All I'm trying to do is return a simple string - but the "error" callback is always called with an error of parseerror. The XmlHttpRequest has the content string "Test Content" in it - so the controller action is being called and the correct data is being returned. I've tried to set the dataType (to "text", "html", "json"), to use the JsonResult type in the controller, to set the returned content type to "text/plain", to use $.get, $.getJson, etc... nothing works. I feel like I must be missing something very, very simple - especially since google is no help. Ideas?

I can't believe I figured this out - there is a typo in the jquery-1.3.1*vsdoc.js (its in jQuery.httpData if anyone out there is interested - they define the input parameter as filter, then try to reference it as s, which throws an exception). Oddly, if you use the non *vsdoc version, the typo isn't there.
Looks like they released jQuery-1.3.2 with vsdocs - which doesn't have the typo. So I'd say the answer is to just download the update. (jQuery Download Page)
There went a day of my life... hope this helps someone.

did you get this article in your travels into the great google void?
http://encosia.com/2008/06/05/3-mistakes-to-avoid-when-using-jquery-with-aspnet-ajax/
he also has many other articles about asp.net and jquery interaction.
Did you try POSTing instead of GETing? as indicated here: http://weblogs.asp.net/scottgu/archive/2007/04/04/json-hijacking-and-how-asp-net-ajax-1-0-mitigates-these-attacks.aspx

Related

Can we call ASP.NET webservice without soap envelop? Like a simple REST api

Can we create a simple post method in ASP.NET web service which runs like a REST API method?
Because I don't want to provide an envelop for post and get request.
Can anyone provide a solution for this?
Not only can you do this, but in fact, if you create a web method in .net, right out of the box:
It can accept soap 1.1, 1.2 - no extra code on your part
It can accept a rest call - even json data - no extra code on your part
It can accept a plane jane URL (again rest) call - no extra code on your part
So, in fact, you get all 3 of the above - in fact the above counts as 4, since your ajax call can use (data) or the URL to pass the information.
Say, I add a I create a web service page like this:
Do note, you don't HAVE to crate a web service page, you can drop into a EXISTING page a function (returns values), or drop in a sub into that page. As long as you ta the function/sub with WebMethod(), then you are good to go.
So, with above page, lets add a simple function. It will take a paramter FirstName, and LastName, and return the full name.
So, we have this code in the above web service page:
<WebMethod()>
Public Function GetName(FirstName As String, LastName As String) As String
Dim sResult As String = ""
sResult = FirstName & " " & LastName
Return sResult
End Function
Above is VERY simple.
So, you can use jQuery, and call this method via post like this:
<asp:Button ID="cSoap" runat="server" Text="Soap Example"
OnClientClick="webcalltest();return false" />
<script>
function webcalltest() {
var First = 'Albert'
var Last = 'Kallal'
var url = '../WebService1.asmx/GetName'
$.ajax({
type: "POST",
url: url,
dataType: "json",
data: JSON.stringify({ FirstName: First, LastName: Last }),
contentType: "application/json; charset=utf-8",
success: function (msg) {
alert(msg.d)
},
error: function (msg) {
alert("Failed: " + msg.status + ": " + msg.statusText);
}
});
As noted, that web method will also accept soap calls.
And you can even skip the headers (in the above post), and toss out a 100% plane jane REST call - just the URL, say like this:
<asp:Button ID="Button2" runat="server" Text="Soap Example"
OnClientClick="webtest2();return false" />
<script>
function webtest2() {
var First = 'Albert'
var Last = 'Kallal'
var url = '../WebService1.asmx/GetName?FirstName=' + First + '&LastName=' + Last
$.get(url, function (data, status) {
alert($(data).find('string').text())
alert("Data: " + data + "\nStatus: " + status);
})
}
</script>
So, in above, we just used the URL for the rest call. (and did not set up any headers, or even a soap xml record). So, just keep in mind, even older legacy soap methods in most cases can be passed and consume JSON data - and you in general don't have to re-write or even change the web methods.
And as above shows, you can just feed the URL with the parameters, and not pass any headers at all - let alone the soap ones.

Scraping Javascript generated data

I'm working on a project with the World Bank analyzing their procurement processes.
The WB maintains websites for each of their projects, containing links and data for the associated contracts issued (example). Contract-related data is available under the procurement tab.
I'd like to be able to pull a project's contract information from this site, but the links and associated data are generated using embedded Javascript, and the URLs of the pages displaying contract awards and other data don't seem to follow a discernable schema (example).
Is there any way I can scrape the browser rendered data in the first example through R?
The main page calls a javascript function
javascript:callTabContent('p','P090644','','en','procurement','procurementId');
The main thing here is the project id P090644. This together with the required language en are passed as parameters to a form at http://www.worldbank.org/p2e/procurement.html.
This form call can be replicated with a url http://www.worldbank.org/p2e/procurement.html?lang=en&projId=P090644.
Code to extract relevant project description urls follows:
projID<-"P090644"
projDetails<-paste0("http://www.worldbank.org/p2e/procurement.html?lang=en&projId=",projID)
require(XML)
pdData<-htmlParse(projDetails)
pdDescribtions<-xpathSApply(pdData,'//*/table[#id="contractawards"]//*/#href')
#> pdDescribtions
href
#"http://search.worldbank.org/wcontractawards/procdetails/OP00005718"
href
#"http://search.worldbank.org/wcontractawards/procdetails/OP00005702"
href
#"http://search.worldbank.org/wcontractawards/procdetails/OP00005709"
href
#"http://search.worldbank.org/wcontractawards/procdetails/OP00005715"
it should be noted that excel links are provided which maybe of use to you also. They may contain the data you intend to scrap from the description links
procNotice<-paste0("http://search.worldbank.org/wprocnotices/projectdetails/",projID,".xls")
conAward<-paste0("http://search.worldbank.org/wcontractawards/projectdetails/",projID,".xls")
conData<-paste0("http://search.worldbank.org/wcontractdata/projectdetails/",projID,".xls")
require(gdata)
pnData<-read.xls(procNotice)
caData<-read.xls(conAward)
cdData<-read.xls(conData)
UPDATE:
To find what is being posted we can examine what happens when the javascript function is called. Using Firebug or something similar we intercept the request header which starts:
POST /p2e/procurement.html HTTP/1.1
Host: www.worldbank.org
and has parameters:
lang=en
projId=P090644
Alternatively we can examine the javascript at http://siteresources.worldbank.org/cached/extapps/cver116/p2e/js/script.js and look at the function callTabContent:
function callTabContent(tabparam, projIdParam, contextPath, langCd, htmlId, anchorTagId) {
if (tabparam == 'n' || tabparam == 'h') {
$.ajax( {
type : "POST",
url : contextPath + "/p2e/"+htmlId+".html",
data : "projId=" + projIdParam + "&lang=" + langCd,
success : function(msg) {
if(tabparam=="n"){
$("#newsfeed").replaceWith(msg);
} else{
$("#cycle").replaceWith(msg);
}
stickNotes();
}
});
} else {
$.ajax( {
type : "POST",
url : contextPath + "/p2e/"+htmlId+".html",
data : "projId=" + projIdParam + "&lang=" + langCd,
success : function(msg) {
$("#tabContent").replaceWith(msg);
$('#map_container').hide();
changeAlternateColors();
$("#tab_menu a").removeClass("selected");
$('#'+anchorTagId).addClass("selected");
stickNotes();
}
});
}
}
examining the content of the function we can see it is simply posting relevant parameters to a form then updating the webpage.
I am not sure I have understood every details of your problem.
But what I know for sure is that casperJS works great for javascript generated content.
You can have a look at it here: http://casperjs.org/
It's written in Javascript and has a bunch of useful functions very well documented on the link I provided.
I have used it myself lately for a personal project and can be set up easily with a few lines of code.
Give it a go!
Hope, that helps..

autocomplete with asp.net generated json

I have the folowing script which does not seem to work. The aspx page returns json similar to the json in the script below which has been commented out. If I stick that json directly into the source as an array, it works perfectly.
But as soon as I try to use the script below, I get no error messages or anything, nothing happens when i type into the autocomplete field.
$(document).ready(function(){
$('#button').click(function() {
alert($("#txtAllowSearchID").val());
});
//var $local_source = [ {id:0,value:"c++"}, {id:1,value:"java"}, {id:2,value:"php"}, {id:3,value:"coldfusion"}, {id:4,value:"javascript"}, {id:5,value:"asp"}, {id:6,value:"ruby"} ];
$("#txtAllowSearch").autocomplete({
source: function(request, response) {
$.ajax({
type: "POST",
url: "test_array.aspx",
data: "{'prefixText': '" + $('#txtAllowSearch').val() + "'}",
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function(data) {
response(data.d);
},
failure: function(errMsg) {
$('#errMessage').text(errMsg);
}
});
},
select: function (event, ui) {
$("#txtAllowSearch").val(ui.item.value); // display the selected text
$("#txtAllowSearchID").val(ui.item.id); // save selected id to hidden input
}
});
});
EDIT: I think the problem is in the aspx page:
objSQLCommand = New SqlCommand("select id, value from table1 where value like '%#prefixText%'", objSQLConnection)
objSQLCommand.Parameters.Add("#prefixText", SqlDbType.VarChar, 255).Value = "ing"
I believe that you have to make the source of the ajax call to a web service, which in your case seems to be a Page Method. So if you have a function in your code behind page look like this:
public static List<string> GetData(string prefixText){ }
You would need to decorate that method with [WebMethod] from the System.Web.Services namespace.
So it would finally look like:
using System.Web.Services;
...
[WebMethod()]
public static List<string> GetData(string prefixText){ }
HTH
EDIT: also you would need to update your ajax call to look like this for the source:
source: 'test_array.aspx/GetData'
It looks to me like the issue might be the source function. When you make the ajax call, it is done asynchronously. So, the function kicks off the ajax call and continues, which returns nothing for the source.
I discovered this when I was doing loading screens. If I put the close for the loading screen after the ajax call, the loading screen would not appear. I had to move it inside the ajax call success and error events to get it to appear and disappear correctly.
Your source should just be a reference to the url
source: "test_array.aspx",
From the documentation:
The datasource is a server-side script
which returns JSON data, specified via
a simple URL for the source-option.

jQuery ajax request response is empty in Internet Explorer

I'm doing the following ajax call:
//exif loader
function LoadExif(cImage) {
$.ajax({
type: "POST",
url: "http://localhost:62414/Default1.aspx/GetImageExif",
data: "{iCurrentImage:" + cImage + "}",
contentType: "application/json; charset=utf-8",
dataType: "json",
error: ajaxFailed,
success: function (data, status) {
var sStr = '';
for (var count in data.d) {
sStr = sStr + data.d[count];
};
alert(sStr);
}
});
};
In Firefox the request works really fine. When I try to run the code in Internet Explorer, the response is empty.
Here is the webmethod witch is called:
<WebMethod()> _
<ScriptMethod(ResponseFormat:=ResponseFormat.Json)> _
Public Shared Function GetImageExif(ByVal iCurrentImage As Integer) As String
Dim sbTable As New StringBuilder
sbTable.AppendLine("<table>")
sbTable.AppendLine("<tr>")
sbTable.AppendLine("<td>Name</td><td>" & gGallery.Images(iCurrentImage).File.Name & "</td>")
sbTable.AppendLine("</tr>")
sbTable.AppendLine("</table>")
Return sbTable.ToString
End Function
Any ideas?
Jan
When you have alert(data); what do you see?
Thanks for this hint. This was the issue. I've copied the code from another of my projects.
alert(data.d);
... works in both browsers. Thanks for the quick answers.
Jan
Starting with ASP.NET 3.5 MS introduced a security feature where they encapsulate any JSON response in a parent object ("d").
Doing this helps against a XSS vulnerability (described here: http://haacked.com/archive/2009/06/25/json-hijacking.aspx)
That's the reason why it exists.
Here's how to handle it, if you code against multiple versions of ASP.NET, you can simply use the following check in your success function to detect it's existence:
if (data.hasOwnProperty('d'))
//use data.d in your code;
else
//use data in your code
As for why Firefox handles it appropriately and IE8 does not, I would assume it has something to do how each parses the JSON object.

Generating an action URL in JavaScript for ASP.NET MVC

I'm trying to redirect to another page by calling an action in controller with a specific parameter. I'm trying to use this line:
window.open('<%= Url.Action("Report", "Survey",
new { id = ' + selectedRow + ' } ) %>');
But I couldn't make it work; it gives the following error:
CS1012: Too many characters in character literal.
Can't I generate the action URL this was on the client side? Or do I have to make an Ajax call by supplying the parameter and get back the needed URL? This doesn't seem right, but I want to if it's the only way.
Is there an easier solution?
Remember that everything between <% and %> is interpreted as C# code, so what you're actually doing is trying to evaluate the following line of C#:
Url.Action("Report", "Survey", new { id = ' + selectedRow + ' } )
C# thinks the single-quotes are surrounding a character literal, hence the error message you're getting (character literals can only contain a single character in C#)
Perhaps you could generate the URL once in your page script - somewhere in your page HEAD, do this:
var actionUrl =
'<%= Url.Action("Report", "Survey", new { id = "PLACEHOLDER" } ) %>';
That'll give you a Javascript string containing the URL you need, but with PLACEHOLDER instead of the number. Then set up your click handlers as:
window.open(actionUrl.replace('PLACEHOLDER', selectedRow));
i.e. when the handler runs, you find the PLACEHOLDER value in your pre-calculated URL, and replace it with the selected row.
I usually declare a javascript variable in the section to hold the root of my website.
<%="<script type=\"text/javascript\">var rootPath = '"
+ Url.Content("~/") + "';</script>" %>
To solve your problem I would simply do
window.open(rootPath + "report/survey/" + selectedRow);
Could you do
window.open('/report/survey/' + selectedRow);
instead where selected row I assume is the id? The routing should pick this up fine.
or use getJSON
Perhaps you could use JSONResult instead. Wherever the window.open should be call a method instead i.e. OpenSurveyWindow(id);
then add some jquery similar to below
function OpenSurveyWindow(id){
$.getJSON("/report/survey/" + id, null, function(data) {
window.open(data);
});
}
now in your controller
public JsonResult Survey(int id)
{
return Json(GetMyUrlMethod(id));
}
That code isnt syntactically perfect but something along those lines should work
Just if someone is still looking for this. The controller action can have normal parameters or a model object with these fields. MVC will bind the valus automatically.
var url = '#Url.Action("Action", "Controller")';
$.post(url, {
YearId: $("#YearId").val(),
LeaveTypeId: $("#LeaveTypeId").val()
}, function (data) {
//Do what u like with result
});
You wont be able to do this, the URL.Action is a server side process so will parse before the clientside selectedRow is available. Israfel has the answer I would suggest.
If selectedRow is a client-side variable, it won't work. You have to use #Israfel implementation to link. This is because the server-side runs before the client-side variable even exists.
If selectedRow is a server-side variable within the view, change to this:
window.open('<%= Url.Action("Report", "Survey", new { id = selectedRow } ) %>');
This is because the id will infer the selectedRow variable type, or you could convert to string with ToString() method (as long as it's not null).
A way to do this that might be considered cleaner involves using the T4MVC T4 templates. You could then write the JavaScript inside your view like this:
var reportUrl = '<%= Url.JavaScriptReplacableUrl(MVC.Survey.Report())%>';
reportUrl = myUrl.replace('{' + MVC.Survey.ReportParams.id + '}', selectedRow);
window.open(reportUrl);
The advantage of this is that you get compile-time checking for the controller, action, and action parameter.
One more thing that can be done, no so clean i guess:
var url = "#Url.RouteUrl(new { area = string.Empty, controller = "Survey", action = "Report" })";
var fullUrl = url + '?id=' + selectedRow;
The easiest way is to declare a javascript variable than concate your parameter value to the link.
var url = '#Url.Action("Action","Controller")' + "/" + yourjavascriptvariable;
<a href='" + url + "'>

Resources