Handling AJAX Page Method Server Errors

On my project we make use of jQuery to make AJAX calls to our web server. On the server we use aspnet page methods to serve data to our jQuery AJAX calls and process updates sent from jQuery. One interesting aspect of this is error handling.

Since we want to avoid any leakage of application information in the case of an error, we’ve taken the approach of putting a try/catch block around all functionality in a page method. In the case of an error we log the original error and then throw a friendly exception so the jQuery AJAX caller knows something has gone wrong, but get no exception detail. Here’s an example:

[WebMethod]
        public static string SaveCustomerInformation(string customerID, 
                                                string customerCode, 
                                                string reasonForCall, 
                                                string description)
        {
            string returnValue = string.Empty;
            try
            {
                bool isValidcustomerCode = true;
                customerCode = customerCode.Trim();
                
                //--validate input data
                if (!string.IsNullOrEmpty(customerCode) && (customerCode.Length < 3 || customerCode.Length > 15))
                {
                   throw new WebMethodException(Content.ReusableContentHelper.customer_CODE_LENGTH_ERROR);
                }
                else if (!string.IsNullOrEmpty(customerCode) && customerCode.ToLower().Equals(customerCodeLoginHelper.SERVICE_REQUEST_customer_CODE))
                {
                    throw new WebMethodException(Content.ReusableContentHelper.customer_CODE_CANNOT_USE_REQUESTED_ERROR);
                }
                //--End Validation
                
                //--Do Work
                CustomerImpl customerImpl = new customerImpl()
                {
                    customerID = int.Parse(JavaScriptHelper.DecryptValueAndHtmlDecode(customerID)),
                    customerCode = customerCode,
                    customerName = reasonForCall,
                    customerDescription = description
                };

                Factory.GetCustomerServices().SavecustomerInformation(customerImpl, currentUser);
                //--End Work                
                
            }
           catch (Exception e)
          {
               //--Log Exception
                Logger().LogException(e, ErrorLogImpl.ApplicationTypeEnum.Website, RidgeTool.BusinessInterfaces.Enums.SeverityLevelEnum.Error, HttpContext.Current.Request.Url.PathAndQuery, "customer.SavecustomerInformation()", currentUser);
               //--Throw Friendly To Caller
                throw new WebMethodException("An error occured in SaveCustomerInformation");
          }
          return returnValue;
        }

In our javascript we wrap all AJAX calls in a common method allowing callers to provide a method to handle processing if an error occurs, and if no error handler is passed then an common error handling routine is run showing an alert box telling the user a problem has occurred. Here’s what our jQuery AJAX call wrapper looks like.

[javascript]
function AJAXCall(url, data, successCallBackFunction, errorCallBackFunction, completeCallBackFunction)
{
data = JSON.stringify(data, UndefinedValueCheck);

$.ajax({
type: "POST",
url: url,
data: data,
contentType: "application/json; charset=utf-8",
dataType: "json",
error: function(xhr, status, exception)
{
var err = eval("(" + xhr.responseText + ")");
if (errorCallBackFunction != null)
{
errorCallBackFunction(err.Message);
}
else
{
alert(err.Message);
}
},
success: function(result)
{
if(successCallBackFunction != null)
{
//–eliminates .d from return values
if (result.d != null)
{
successCallBackFunction(result.d);
}
else
{
successCallBackFunction(result);
}
}
},
complete: function()
{
if(completeCallBackFunction != null)
{
completeCallBackFunction();
}
}
});
}
[/javascript]

As you can see the user can choose to provide or not provide an error handling function and if they don’t user still is alerted a problem has occurred.

Here’s an example of our jQuery AJAX wrapper in use:

[javascript]
var text = $("#testingJavascriptTextBox").val();
var passedData = "{‘testString’: ‘" + replaceForJavascript(text) + "’}";
var postData = new Object();
postData.testString = text;

AJAXCall("/WebMethods/Tags.aspx/TestingReplace", postData, function(textInput)
{
alert(textInput.d);
$("#testingJavascriptSpan").append(replaceForHTML(textInput.d));
$("#resultsJavascriptTextBox").attr("value", textInput.d);
});
[/javascript]

What all this means is that we protect ourselves form leaking application details in errors by never allowing a naked error to pass through an AJAX call to our web clients. We still alert our AJAX caller to the existence of an error, so we can still know on the rare occasion (hopefully) when one happens!