C# ASP.Net autocomplete with Javascript/Json post not working - javascript

I am currently working with a c# asp.net usercontrol in which I need a functional autocomplete.
The script looks like it wants to run, as you can see; the progress bar spins, but it ALWAYS comes back 'Error'. I'm not sure what to do from here. I followed at least 5 different tutorials to get this working; the code mimics the code found here; but it doesn't seem to work when all is said and done. What am I missing? Any suggestions to get me where I need to be would be much appreciated.
If any more information is needed let me know, but the entire code can be found below.
HTML/Javascript
<%# Control Language="C#" AutoEventWireup="true" CodeBehind="Search Textbox.ascx.cs" Inherits="StagingApplication.Search.Search_Textbox" %>
<link href="../css/styleCustomPages.css" rel="stylesheet" />
<link href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.1/themes/base/jquery-ui.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.1/jquery-ui.min.js"></script>
<script type="text/javascript">
$(document).ready(function () {
SearchText();
});
function SearchText() {
$(".autosuggest").autocomplete({
source: function (request, response) {
$.ajax({
type: "POST",
contentType: "application/json; charset=utf-8",
url: "Search_Textbox.aspx/GetAutoCompleteData",
data: "{'searchText':'" + document.getElementById('txtSearch').value + "'}",
dataType: "json",
success: function (data) {
response(data.d);
},
error: function (result) {
alert('Error' );
}
});
}
});
}
</script>
<div class="ui-widget">
<label for="tbAuto">Enter UserName: </label>
<input type="text" id="txtSearch" class="autosuggest" />
</div>
C# Code Behind
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Data;
using System.Data.SqlClient;
using System.Web.Services;
using System.Web.Script.Services;
namespace StagingApplication.Search
{
public partial class Search_Textbox : System.Web.UI.UserControl
{
#region Declarations
static string dbSearch = "db string";
#endregion
protected void Page_Load(object sender, EventArgs e)
{ }
#region Methods and Functions
[WebMethod, ScriptMethod]
public static List<string> GetAutoCompleteData(string searchText)
{
List<string> result = new List<string>();
using (SqlConnection con = new SqlConnection(dbSearch))
{
using (SqlCommand cmd = new SqlCommand("SELECT TOP 1000 [SearchTerm] FROM [Search].[dbo].[Cache] where AutoCompleteTerm = 0 and SearchTerm LIKE #SearchText + '%';", con))
{
con.Open();
cmd.Parameters.AddWithValue("#SearchText", searchText);
SqlDataReader dr = cmd.ExecuteReader();
while (dr.Read())
{
result.Add(dr["SearchTerm"].ToString());
}
return result;
}
}
}
}
}

cmd.Parameters.AddWithValue("#SearchText", searchText);
Variable searchText doesn't exist, because:
Your function begins with this:
public static List<string> GetAutoCompleteData(string username)
It should be:
public static List<string> GetAutoCompleteData(string searchText)
You forgot to update username to searchText.

In the aspx page, you need to declare a WebMethod which will call the code in the user control. This is how I do it in one of our sites:
[WebMethod]
public static WebMethodReturn<IEnumerable> GetWatchboxes()
{
return AddOns_WatchboxAdmin.GetWatchboxes();
}
...making sure in jQuery to call the page method url of the aspx page not the control.
My answer here may help a little bit too, particularly the recommendation to debug web traffic with Fiddler.
Its important to note that your "Error" string is in the jQuery error block. That means it's probably a transport error or some sort of error in jQuery. My guess is 404 because you don't have a page method in the aspx page only in the user control. This error block won't catch server side exceptions. I return a little wrapper class with a Data property and an Error property; I put any exception string into the Error property otherwise I leave it null. I check this string clientside. This is optional, just a little tip that jQuery knows nothing about .NET exceptions - not that I believe an exception is the problem here.
The ScriptMethod attribute is needed in ASMX web services in order to return JSON but for reasons unknown to me you don't need it in page methods.
You certainly do not need to be doing any manual serialisation to JSON as instructed in another answer. If called correctly by jQuery, the example I have given will return JSON with no further work needed.
So, to summarise, put this into your aspx.cs:
[WebMethod]
public static List<string> GetAutoCompleteData(string searchText)
{
and call and return the results of the same function in the instance of the user control on your aspx page (or make the function public static in the user control). Inspect the client call and server response in Fiddler in case of any problems.

Since you're expecting JSON to be returned, you may need to try something like this:
Add the following declaration:
using System.Web.Script.Serialization;
Then perform the following on your List<string> result variable:
JavaScriptSerializer serializer = new JavaScriptSerializer();
serializer.MaxJsonLength = Int32.MaxValue; // optional
return serializer.Serialize(result);
UPDATE - here's a more complete solution for outputting your data to JSON:
public static string GetDataTableToJSONString(DataTable table) {
List<Dictionary<string, object>> list = new List<Dictionary<string, object>>();
foreach (DataRow row in table.Rows) {
Dictionary<string, object> dict = new Dictionary<string, object>();
foreach (DataColumn col in table.Columns) {
dict[col.ColumnName] = row[col];
}
list.Add(dict);
}
JavaScriptSerializer serializer = new JavaScriptSerializer();
serializer.MaxJsonLength = Int32.MaxValue;
return serializer.Serialize(list);
}
public static List<string> GetAutoCompleteData(string searchText) {
string sql = "SELECT TOP 1000 [SearchTerm] FROM [Search].[dbo].[Cache] " +
"where AutoCompleteTerm = 0 and SearchTerm LIKE #SearchText + '%';";
using (SqlConnection con = new SqlConnection(dbSearch)) {
con.Open();
using (SqlCommand cmd = new SqlCommand(sql, con)) {
cmd.Parameters.AddWithValue("#SearchText", searchText);
using (SqlDataAdapter da = new SqlDataAdapter(cmd)) {
using (DataSet ds = new DataSet()) {
da.Fill(ds);
return GetDataTableToJSONString(ds.Tables[0]);
}
}
}
}
}

Related

How to load with jquery json data from a backing bean in a custom JSF component?

In short, what is the best way in JSF 2.2 to load json data from a backing bean into a javascript prog that runs in a browser.
I'm porting a dirty hacked iframed visjs network graph to JSF 2.2 - Primefaces 6.1. We have all special tags in a jsf tag library as custom UiComponent's in a jar module. I added a new graph tag, an extended UiComponentBase class, to the tag library and put all visjs javascript files with #ResourceDependency to the class. The tag loads fine, but jquery try to open a url to load json formatted graph coordinates:
$.ajax({
url: "/ajax/getNetwork",
type: "POST",
data: "",
dataType: "json",
success: showNetwork,
error: showError
});
On the old iframe solution, visjs load all data via this url.
I read some things about a single xhtml page with an <h:outputText>, a servlet or a JAXRS rest service endpoint, but these solutions does not fit into a taglibrary and must be configurated in the web.xml of the web project. Is there a way to do it with ajax events, or an ajaxBehavior in a tag library?
Thanks in advance.
The graph UiComponent in the taglib works now as expected. A extended datamodel is filled in a backing bean, similar like the lazy datamodel of the primefaces datatable. The jQuery url request from the client side in the web browser is catched with a custom PhaseListener. There was no way to access the UiComponent from the PhaseListener, because the UiViewRoot component tree was empty. So I put the datamodel in the graph UiComponent class into the SessionMap and can access the datamodel in the PhaseListener. I'm not sure that is the best way, to do this. Here is the custom PhaseListener:
public class GraphPhaseListener implements PhaseListener {
private TopologyModel topoModel;
private PhaseId phaseId = PhaseId.RENDER_RESPONSE;
#Override
public void beforePhase(PhaseEvent event) {
FacesContext context = event.getFacesContext();
Object obj = context.getExternalContext().getRequest();
if(!(obj instanceof HttpServletRequest)) {
return;
}
HttpServletRequest request = (HttpServletRequest) obj;
if(!("true").equals(request.getHeader("networkAjax")) || !request.getMethod().equals("POST")) {
return;
}
Map<String, Object> sessionMap = event.getFacesContext().getExternalContext().getSessionMap();
Object object = sessionMap.get(EnhancedGraphRenderer.GRAPH_TOPOLOGIE_KEY);
if(object == null || !(object instanceof TopologyModel)) {
return;
}
topoModel = (TopologyModel) object;
String graphAction = request.getHeader("graphAction");
String actionResponse = "";
if(graphAction==null) {
return;
}
switch(graphAction) {
case "getNetwork":
actionResponse = topoModel.getJsonNetwork();
break;
case "getNodeTypes":
actionResponse = topoModel.getJsonNodeTypes();
//actionRespoonse = topoModel.getFromAction("{node_type:switch, node_id: 2, request: children}")
break;
default:
actionResponse = "{}";
}
HttpServletResponse response = (HttpServletResponse) context.getExternalContext().getResponse();
try {
PrintWriter output = response.getWriter();
output.print(actionResponse);
context.responseComplete();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
#Override
public void afterPhase(PhaseEvent arg0) {
}
#Override
public PhaseId getPhaseId() {
return phaseId;
}
}
I hope that helps others, sugestions for improvement are welcome.

An asynchronous operation cannot be started at this time when calling FatSecret Sharp API

I'm attempting to make a basic ASP.NET application that makes a call to the Fatsecret API using a wrapper called FatSecret Sharp, however am getting this error when I try to make the server side method call from my JS script, and I'd like to determine how I can successfully use this wrapper to create a web application.
You'll notice that the API call from the wrapper explicitly mentions that it is "synchronous", so I assume that's what's throwing the error, I just don't know why, or how I can use that call successfully with a web application.
Here is my code:
Javascript
var jsonData;
function search() {
var element = document.getElementById("searchField")
var searchTerm = element.value;
callAJAX("FoodSearchExample", searchTerm);
}
function callAJAX(requestMethod, term) {
var pageMethod = "default.aspx/" + requestMethod;
$.ajax({
url: pageMethod,
data: JSON.stringify({ searchTerm : term }),
type: "POST",
contentType: "application/json",
dataType: "JSON",
timeout: 600000,
success: function (result) {
ajaxCallback(result.d);
},
error: function (xhr, status) {
alert(status + " - " + xhr.responseText);
}
});
return false;
}
function ajaxCallback(serverResponse) {
if (serverResponse !== "loadLocations") {
//jsonData = JSON.parse(serverResponse);
alert(serverResponse);
}
else
alert("error");
}
C#
namespace HELP_Testing
{
public partial class _default : System.Web.UI.Page
{
private static string consumerKey = "key (removed from question)";
private static string consumerSecret = "secret (removed from question)";
[WebMethod]
public static string FoodSearchExample(string searchTerm)
{
FoodSearch foodSearch = new FoodSearch(consumerKey, consumerSecret);
string str = "";
var response = foodSearch.GetResponseSynchronously(new FoodSearchRequest()
{
SearchExpression = searchTerm
});
List<Food> foods = new List<Food>();
if (response.HasResults)
{
Food f;
foreach (var food in response.foods.food)
{
f = new Food();
f.FoodId = food.food_id;
f.FoodType = food.food_type;
f.FoodName = food.food_name;
foods.Add(f);
}
}
else
Console.WriteLine("No results from term");
JavaScriptSerializer serializer = new JavaScriptSerializer();
str = serializer.Serialize(foods);
return str;
}
protected void Page_Load(object sender, EventArgs e)
{
}
}
}
HTML
<%# Page Language="C#" AutoEventWireup="true" Async="True" CodeBehind="default.aspx.cs" Inherits="HELP_Testing._default" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<script type="text/javascript" src="scripts/default.js"></script>
<script type="text/javascript" src="scripts/jquery-1.11.1.js"></script>
<title>Healthy Eating Life Planner</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<input type="text" name="Food Search" id="searchField" />
<button type="submit" onclick="search()">Search</button>
</div>
</form>
</body>
</html>
The full error message is:
An asynchronous operation cannot be started at this time. Asynchronous operations may only be started with an asynchronous handler or module
or during certain events in the Page lifecycle. If this exception occurred while executing a Page, ensure that the Page
is marked Async = true. This exception may also indicate an attempt to call an 'async void' method, which is generally
unsupported within ASP.NET request processing. Instead, the asynchronous method should return a Task, and the caller should await it"
Yes, the problem is in GetResponseSynchronously. Polling for completion is a very questionable approach.
There are a couple of ways to solve this. One is to throw out the FatSearch CSharp library and use HttpClient to write to their JSON API. This approach is cleaner, but means you'll have to write more code.
The other approach is to wrap the sort-of-EBAP APIs from FatSearch CSharp as async-compatible methods. In this case, the important members are GotResult, GotError, and StartRequestAsync. Note that your web method will become async.
Instead of calling
public Task GetResponseSynchronously(TRequest request) like the sample console App suggests, in a Web context such as MVC, it is better to add an async method such as the one I successfully wrote below:
/// <summary>
/// Gets the response Asynchronously.
/// </summary>
/// <param name="request">The request.</param>
/// <returns>A parsed response, or throws an exception.</returns>
public async Task<TResponse> GetResponseAsynchronously(TRequest request)
{
var requestUrl = CreateRequestUrl(request);
HttpClient APIRequest = new HttpClient();
var response = await APIRequest.GetAsync(requestUrl).ConfigureAwait(false);
response.EnsureSuccessStatusCode();
string downloadedString = await response.Content.ReadAsStringAsync();
var result = ConvertClientResultString(downloadedString);
return result;
}
It's important to note that to obtain a seamless result, you will need to alter the BaseJsonService.cs of the Service4u2Lib by adding a response processing method like the one below:
/// <summary>
/// Handles the client result string.
/// </summary>
/// <param name="downloadedString">The downloaded string.</param>
public TResultType ConvertClientResultString(string downloadedString)
{
// Check for html doctype and report error if found.
int takeLength = downloadedString.Length > 20 ? 20 : downloadedString.Length;
if (downloadedString.Substring(0, takeLength).Contains("!DOCTYPE html"))
HandleClientError(new NotSupportedException("The service call returned html and not json"));
var result = new TResultType();
string json = downloadedString;
if (result is IJSONMassager)
{
json = ((IJSONMassager)result).MassageJSON(downloadedString);
}
if (result is IJSONSelfSerialize<TResultType>)
{
result = ((IJSONSelfSerialize<TResultType>)result).SelfSerialize(json);
}
else
result = JsonHelper.Deserialize<TResultType>(json);
if (GotResult != null)
GotResult(this, new EventArgs<TResultType>() { Argument = result });
return result;
}
Basically we reshuffle the existing objects to make them work with an HTTPClient object which can handle requests using the .ConfigureAwait(false); method which ensures that the callback happens.

How can I send complex JavaScript object to ASP.net WebMethod?

I'm trying send my client-side custom object (JavaScript) to ASP.net Web Method. I use jQuery Ajax command to perform this operation.
There a example of my object:
function Customer() {
this.Name = "";
this.Surname = "";
this.Addresses = new Array();
}
I load data with this method:
function buildCurrentCustomer() {
var currentCustomer = new Customer();
/** General Info **/
currentCustomer.Name = $("#Name").val();
currentCustomer.Surname = $("#Surname").val();
currentCustomer.Addresses = new Array();
currentCustomer.Addresses["HOME"] = $("#adHome").val();
currentCustomer.Addresses["OFFICE"] = $("#adOffice").val();
return currentCustomer;
}
And finally I send data with this code:
$.ajax({
type: "POST",
url: "../_layouts/CustomerManager/MasterPage.aspx/SetCustomer",
contentType: "application/json; charset=utf-8",
dataType: "json",
data: "{customer: " + JSON.stringify(currentCustomer) + "}",
cache: false,
success: function (result) {
},
error: function (ex) {
WriteToConsole(ex.responseText);
}
});
My server-side methods is like that:
[WebMethod]
public static bool SetCustomer(CustomerModel Customer)
{
//My code...
}
and my CustomerModel class is like that:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Common.Model.JavaScriptModel
{
public class CustomerModel
{
/** General Info **/
public string Name {get;set;}
public string Surname {get;set;}
public Dictionary<string, string> Addresses { get; set; }
}
}
The problem is that when I execute Ajax Call server-side method doesn't execute. If I change signature of server-side method in:
public static bool SetCustomer(List<CustomerModel> Customer)
SetCustomer method is executed but the List is empty.
Why have I this problem? Where can I find documentation about this functionality?
Thanks
first, if you use the data like this
data: "{customer: " + JSON.stringify(currentCustomer) + "}",
on the code behind, you need to take the same parameter customer and not Customer, so this
public static bool SetCustomer(CustomerModel Customer) { ... }
needs to be changed to
public static bool SetCustomer(CustomerModel customer) { ... }
second, your Customer object in the javascript is like this if translated to asp.net
string Name;
string Surname;
List<string> Addresses;
but your class in the code behind for Addresses is using
Dictionary<string, string>
thus causing your data from client side can't be parsed in the server side and return an error to the client side, so you need to change your Addresses class to
public List<string> Addresses { get; set; }
and lastly, your code inside buildCurrentCustomer for the Addresses is being set like this
currentCustomer.Addresses = new Array();
currentCustomer.Addresses["HOME"] = $("#adHome").val();
currentCustomer.Addresses["OFFICE"] = $("#adOffice").val();
this will never add a value to Addresses since it's type is an array, but you set the value to it as if it was an object, so if you want to stick to use an array, you need to change it to
currentCustomer.Addresses = new Array();
currentCustomer.Addresses.push($("#adHome").val());
currentCustomer.Addresses.push($("#adOffice").val());
*Note:
use this if you want to use the Addresses as an array, but if you need the Addresses to be an object that contains HOME and OFFICE, I'll Edit the answer
Edit:
perhaps you can use a javascript object like this
currentCustomer.Addresses = {};
currentCustomer.Addresses["Home"] = $("#adHome").val();
currentCustomer.Addresses["Office"] = $("#adOffice").val();
to make the equivalent for Dictionary<string,string> but if it didn't work you could change your Addresses to class too like this
public List<Address> Addresses { get; set; }
and add class Address
public class Address
{
public string Home {get;set;}
public string Office {get;set;}
}
I myself never used a Dictionary myself, so I don't know if it's the same
You can change your source code like this..
AJAX-
data: JSON.stringify({'customer':currentCustomer});
ASP.net Web Method-
[WebMethod]
public static bool SetCustomer(object customer)
{
CustomerModel CM = new CustomerModel();
_Serializer = new JavaScriptSerializer();
_StringBuilder = new StringBuilder();
_Serializer.Serialize(customer, _StringBuilder);
CM = _Serializer.Deserialize<CustomerModel>(_StringBuilder.ToString());
}
Note that you have to initialize _Serializer and the _StringBuilder properties at the top of the page as global variables...
public static JavaScriptSerializer _Serializer;
public static StringBuilder _StringBuilder;

Error 500 when calling asmx service from javascript

I am calling a .net 2.0 asmx service using javascript and servicereference. When I call the service in the test environment and locally, everything works fine. In production I get an error 500. I don't see any errors in the event log or displayed anywhere.
If I hit the web service directly then I receive the json formatted data. If I call the web service from javascript using the servicereference then I get the error 500.
I think this might be related to a size limitation since I can reduce the number of datapoints and the data is returned through javascript correctly.
I have tried increasing the maxRequestLength in httpRuntime but that doesn't have any affect. Is there some setting that I am missing?
If you view this page: http://bit.ly/NXBFpD you will see that data is returned without an error.
If you view this page and watch the calls being made in firebug you'll see that the same page as above is called, but an error 500 is returned. http://bit.ly/NXBPO1
This is the code-behind on the page being called:
protected void Page_Load(object sender, EventArgs e)
{
if(!IsPostBack)
{
ScriptManager.GetCurrent(Page).Services.Add(new ServiceReference("/Services/FusionService.asmx"));
}
}
}
This is the js code calling the service:
function GetGeometries(sw, ne, metro) {
FusionService.GetGeometries(sw.lat(), sw.lng(), ne.lat(), ne.lng(), metro, drawMap);
}
This is the service:
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.Web.Script.Services.ScriptService]
public class FusionService : System.Web.Services.WebService
{
private string url = "https://www.googleapis.com/fusiontables/v1/query";
[WebMethod]
[ScriptMethod(UseHttpGet = true, ResponseFormat = ResponseFormat.Json)]
public string GetGeometries(decimal southWestLat, decimal southWestLng, decimal northEastLat, decimal northEastLng, string metro)
{
try
{
var jss = new JavaScriptSerializer();
string key = string.Format("Geometry-{0}", metro);
var geometryData = HttpContext.Current.Cache[key] as string;
if (!string.IsNullOrEmpty(geometryData))
{
var deserialized = jss.Deserialize<GeometryData>(geometryData);
return jss.Serialize(deserialized);
}
var query = "SELECT name, geometry, FranchiseI, latitude, longitude, STATE FROM " +
"1JNOz_lhP1B8oCzeNLBm8u5k6ezdRDKtM-NBD8 where ST_INTERSECTS(geometry, RECTANGLE(LATLNG("
+ southWestLat + "," + southWestLng + "), LATLNG(" + northEastLat + "," + northEastLng + ")))";
var requestString = url + "?sql=" + HttpUtility.UrlEncode(query) + "&key=AIzaSyCXLd2VlvZ0FNLbgMKsfSq7Uvp3IDwa";
var request = WebRequest.Create(requestString) as HttpWebRequest;
using (var response = request.GetResponse() as HttpWebResponse)
{
var reader = new StreamReader(response.GetResponseStream());
var json = reader.ReadToEnd();
json = json.Replace("NaN", "\"\"");
HttpContext.Current.Cache[key] = json;
var deserialized = jss.Deserialize<GeometryData>(json);
return jss.Serialize(deserialized);
}
}
catch(Exception ex)
{
return ex.Message;
}
}
}
This was being caused by a limit in the size that can be serialized by JSON. I needed to add this to my web.config.
<system.web.extensions>
<scripting>
<!--The Following lines value should be true in production server-->
<scriptResourceHandler enableCompression="false" enableCaching="true" />
<webServices>
<jsonSerialization maxJsonLength="5000000">
</jsonSerialization>
</webServices>
</scripting>
</system.web.extensions>
Call the URL from the browser on the server and you'll likely see the contents of the error. Alternatively - change your web.config, so you can see the actual error from a client browser as well:
Set:
<customErrors mode="Off" />
This will allow you to track down the issue and resolve it!

Dynamically create JS file using ASP.net Handler

I have many clients I want to give them scripts so I want to Create JS file based on their Cusotmer ID.
So I can return and it directly execute on customer side.
Client can be anyone either PHP,Html, ASP.net
Problem is when i browse this link it give me JS string but on customer side this script is not executing like for testing I put alert this alert is not showing on customer side
Customer
<head>
<script src="http://localhost:12604/JSCreator/Handler.ashx?CustomerID=123" type="text/javascript"></script>
<title></title>
</head>
Handler file
public class Handler : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
string CustomerId = context.Request["CustomerId"].ToString();
string jscontent = JSFileWriter.GetJS(CustomerId); // This function will return my custom js string
context.Response.ContentType = "text/javascript";
context.Response.Write(jscontent);
}
public bool IsReusable
{
get
{
return false;
}
}
}
ContentType should be application/javascript
public void ProcessRequest(HttpContext context)
{
string CustomerId = context.Request["CustomerId"].ToString();
string jscontent = JSFileWriter.GetJS(CustomerId); // This function will return my custom js string
context.Response.ContentType = "application/javascript";
context.Response.Write(jscontent);
}

Categories