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!
Related
I've been playing around with Spring MVC and came across an issue, where I send a JSON object from the frontend with JavaScript to the backend. To do so, I use a POST Method, which seems to return null values on the backend.
JavaScript:
function calcBudget() {
var table = document.getElementById("mainTable");
const jsonBody = [];
for (var i = 1; i < table.rows.length; i++) {
const jsonItem = {};
jsonItem ["name"] = table.rows[i].cells[0].firstChild.value;
jsonItem ["category"] = table.rows[i].cells[1].firstChild.value;
jsonItem ["amount"] = table.rows[i].cells[2].firstChild.value;
jsonBody.push(jsonItem);
}
console.log(JSON.stringify(jsonBody));
var xhr = new XMLHttpRequest();
xhr.open("POST", "/home/test", true);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.send(JSON.stringify({
jsonBody
}));
}
JavaScript Output:
[{"name":"01","category":"01","amount":"03"}]
RestController:
#Controller
public class GreetingsController {
#PostMapping("/home/test")
public String readInput(#RequestBody CalcModel model) {
System.out.println(model.expenseName +" " + model.expenseCategory+" "+ model.expenseAmount);
return "index";
}
}
RestController Output:
null null null
CalcModel:
public class CalcModel {
public String expenseName;
public String expenseCategory;
public Double expenseAmount;
public CalcModel(String expenseName, String expenseCategory, Double expenseAmount) {
this.expenseName = expenseName;
this.expenseCategory = expenseCategory;
this.expenseAmount = expenseAmount;
}
}
Why is the RestController output null? I have attempted to integrate an H2 database, as I assumed I would need one. Turned out, though, that this does not work either. I also tried to use getters for the CalcModel, without any luck. Any ideas?
Edit:
It seems as I'm sending a JSON array to Spring Boot, which expects to find just a single object, not an array of objects. Removing the square brackets seems to solve the issue when testing via Postman. Now I need to find a solution how to make SpringBoot recognize the response body as a JSON array.
Solution:
Change RestController Method to this (Request Body needs to be an ArrayList)
#PostMapping("/home/test")
public String readInput(#RequestBody ArrayList<CalcModel> model) {
for (CalcModel calcModel : model) {
System.out.println(calcModel.name + " " + calcModel.category + " " + calcModel.amount);
}
return "index";
}
In the JavaScript file, remove the curly brackets
xhr.send(JSON.stringify(jsonBody));
It seems like you have different namings in the jsonBody and the CalcModel. Try changing the names to the same thing.
[{"name":"01","category":"01","amount":"03"}]
public class CalcModel {
public String name;
public String category;
public Double amount;
You might also need a parameterless constructor and getters and setters.
I am very sorry to ask this question. I am very sorry if my question is not clear. I am trying to call .asmx webservice from my AngularJS Services.
In my AngularJS Services, I have the following code snippet:
.factory('BoothDesignatedCoordsService', ['$http',function ($http) {
return {
// Might retrieved from db eventually
fnGetBoothDesignatedCoords: function (strBoothName, intFloorPlanID) {
try
{
var JSONObj = {
BoothName: strBoothName,
FloorPlanID: intFloorPlanID
};
var sendToWS = JSON.stringify(JSONObj)
}
catch(e)
{
alert("error from fnGetBoothDesignatedCoords " + e)
}
$http.post('http://localhost:4951/wsIPS.asmx?op=fnGetBoothDesignatedCoords', sendToWS).then(function (response) {
if (response.data)
alert("Post Data Submitted Successfully!");
}, function (response) {
alert("Service not Exists");
alert(response.status);
alert(response.statusText);
alert(response.headers());
});
}
}
}])
In my .asmx file, I have the following code snippet:
[WebMethod(EnableSession = true)]
[ScriptMethod(UseHttpGet = true)]
public void fnGetBoothDesignatedCoords(string objJSONRequest)
{
wsIPS objRequest = JsonConvert.DeserializeObject<wsIPS>(objJSONRequest);
string strBoothName = objRequest.ClientBoothName;
string strFloorPlanID = objRequest.ClientFloorPlanID;
int intFloorPlanID = int.Parse(strFloorPlanID.ToString());
double[] arrBoothDesignatedCoords = new double[0];
string strConnectionString = ConfigurationManager.ConnectionStrings["IPSConnection"].ConnectionString;
SqlConnection myConnect = new SqlConnection(strConnectionString);
string strCommandText = "SELECT DesignatedCoords FROM Booth WHERE Name = #Name AND FloorPlanID = #FloorPlanID";
SqlCommand selectCmd = new SqlCommand(strCommandText, myConnect);
selectCmd.Parameters.AddWithValue("#Name", strBoothName);
selectCmd.Parameters.AddWithValue("#FloorPlanID", intFloorPlanID);
myConnect.Open();
SqlDataReader reader = selectCmd.ExecuteReader();
if (reader.Read())
{
string strBoothDesignatedCoords = reader["DesignatedCoords"].ToString();
arrBoothDesignatedCoords = Array.ConvertAll(strBoothDesignatedCoords.Split(','), double.Parse);
}
myConnect.Close();
JavaScriptSerializer js = new JavaScriptSerializer();
Context.Response.Write(js.Serialize(arrBoothDesignatedCoords));
}
}
}
Note : my .asmx web service is running, so I can use the url to access it directly on my local computer.
However, I receive alert error that say "service not exists", and the error code is 500, and it is internal server error. And the console log gives me this error: TypeError: Cannot convert object to primitive value. Can someone please help me?
This is the screenshot of my .asmx webservice and its url:
while saving form details using backbone i m getting error as
POST http://localhost:8080/gamingengine/restful-services/badges 500 (Internal Server Error)
st.ajaxTransport.sendjquery.js:4
st.extend.ajaxjquery.js:4
Backbone.ajaxbackbone.js:1197
Backbone.syncbackbone.js:1178
_.extend.syncbackbone.js:284
_.extend.savebackbone.js:490
Backbone.Form.extend.saveBadgesbadges.js:219
st.event.dispatchjquery.js:3
st.event.add.y.handle
Uncaught SyntaxError: Unexpected token <
st.extend.parseJSONjquery.js:2
window.clearErrorscommon.js:386
st.event.dispatchjquery.js:3
st.event.add.y.handlejquery.js:3
st.event.triggerjquery.js:3
rjquery.js:4
st.ajaxTransport.send.r
my backbone code is as follows
this.model.save(this.getValue(), {
//beforeSend : setHeader, //added
iframe : true,
wait : true,
files : $file,
elem : this,
data : _.omit(this.getValue(), ['iconFile']),
silent : true,
success : function(model, response, options) {
alert("inside save..");
var error = false;
_.each(response, function(val, key) {
if (app.BadgesView.fields[key]
&& val instanceof Object
&& val.error) {
error = true;
app.BadgesView.fields[key]
.setError(val.message);
}
});
if (!error) {
app.BadgesView.model.set(model);
app.BadgesListCollection.add(model);
return;
}
return false;
},
error : function(model, response, options) {
console.log("error while save in badges.js : ");
}
});
and server side code is as follows which is using resteasy
#POST
#Consumes("multipart/form-data")
#Produces("text/html")
#Cache(noStore = true)
public final Response saveBadges(
#MultipartForm final BadgesForm badgesForm) throws IOException {
System.out.println("saveBadges called........");
final int no_of_coins = badgesForm.getNo_of_coins();
final String badge_name = badgesForm.getBadge_name();
final int score = badgesForm.getScore();
final int badge_id = badgesForm.getBadge_id();
final byte[] iconFile = badgesForm.getIconFile();
final Validator validatorNumeric = ValidationFactory
.getTextFieldNumericValidator();
validatorNumeric.validate("no_of_coins", no_of_coins,
threadlocalExceptions.get());
System.out.println("iconFile :" + iconFile);
if (iconFile.length >= GamingConstants.ONE) {
ValidationFactory.getImageContentValidator().validate("iconFile",
iconFile, threadlocalExceptions.get());
ValidationFactory.getImageSizeValidator().validate("iconFile",
iconFile, // added size // validator
threadlocalExceptions.get());
}
if (threadlocalExceptions.get().isEmpty()) {
try {
final Badges badges = new Badges();
badges.setNo_of_coins(no_of_coins);
badges.setBadge_name(badge_name);
badges.setScore(score);
badges.setBadge_id(badge_id);
final Coin coin = new Coin();
coin.setId(badgesForm.getCoin());
badges.setCoin(coin);
Badges.save(badges);
final Badges badgesObj = new Badges();
badgesObj.setBadge_id(badges.getBadge_id());
badgesObj.setCoin(coin);
badgesObj.setBadge_name(badges.getBadge_name());
badgesObj.setNo_of_coins(badges.getNo_of_coins());
badgesObj.setScore(badges.getScore());
if (iconFile.length >= GamingConstants.ONE) {
final String imgPath = "restful-services/badges/"
+ badges.getBadge_id() + "/image";
badgesObj.setIconPath(imgPath);
final String fileName = path + badges.getBadge_id()
+ ".png";
CommonUtils.writeIcon(iconFile, fileName);
} else {
badgesObj.setIconPath(defaultPath);
}
Badges.update(badgesForm.getBadge_id(), badgesObj);
final gamingengine.bind.Badges bindBadges = new gamingengine.bind.Badges();
bindBadges.setBadge_id(badgesObj.getBadge_id());
bindBadges.setCoin(badgesObj.getCoin());
bindBadges.setNo_of_coins(badgesObj.getNo_of_coins());
bindBadges.setBadge_name(badgesObj.getBadge_name());
bindBadges.setIconPath(badgesObj.getIconPath());
bindBadges.setScore(badgesObj.getScore());
final ObjectMapper mapper = new ObjectMapper();
final String jsonString = mapper.writeValueAsString(bindBadges);
return Response.ok().entity(jsonString).build();
} catch (DBException e) {
if (e.getMessage().startsWith(DBException.PARENT_NOT_EXISTS)) {
final String fieldName = e.getMessage()
.substring(e.getMessage().indexOf("-") + 1).trim()
.toLowerCase();
e.getValidationException()
.setMessage(
"The "
+ fieldName
+ " is already deleted.Please refresh the page ");
threadlocalExceptions.get().put(fieldName,
e.getValidationException());
}
}
}
final Map<String, ValidationException> exceptions = threadlocalExceptions.get();
threadlocalExceptions.remove();
final ObjectMapper mapper = new ObjectMapper();
final String exceptionJsonString = mapper
.writeValueAsString(exceptions);
return Response.ok().entity(exceptionJsonString).build();
}
while saving data of the form, backbone does not call the saveBadges() method of server side code
in chrome network it shows as
badges
/gamingengine/restful-services
POST
500
Internal Server Error
text/html
now i tried as
data:this.getvalue() in save() its sending all values to server except for iconPath
**iconPath : {
type : "FilePicker",
title : "Icon"
}**
and in save() of backbone
**var $file = $('input[name="iconPath"]', this.el);** this two lines are not sending iconPath, its empty any guesses
any help appreciated!!! thanks
The issue could be related to the content-type expected by your service, "multipart/form-data". Backbone by default does not provide an implementation to send a multipart request on the "save" method.
Here is a link with information about how you can send the multipart-request:
multipart form save as attributes in backbonejs
Also, message that you are receiving about the unexpected character ">" could be related to the "dataType" associated to the request, try to change it to "text" to avoid parsing to JSON, adding that you should be getting the correct error.
this.model.save(this.getValue(), {
//beforeSend : setHeader, //added
iframe : true,
wait : true,
files : $file,
dataType: "text",
elem : this,
data : _.omit(this.getValue(), ['iconFile']),
silent : true..
}
I will suggest to review your developer console as well in Chrome, Safari or Firefox to see how the request is been sent to the server, that could give you a better understanding how your request is been received by the server.
Also, try testing your service by external "Restful" tool, chrome provided the "Advance Restful Client" where you can test your service.
Hope this information helps to solve your issue or guide you in the right direction.
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]);
}
}
}
}
}
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.