We have an older ASP.NET WebForms application which performs AJAX request by using jQuery $.ajax()
calls on the client side, calling static methods in the page code-behind decorated with [WebMethod]
attributes.
If an unhandled exception occurs within the WebMethod, it does not fire the Application_Error
event and is thus not picked up by our error logger (ELMAH). This is well known and not a problem - we have all WebMethod code wrapped in try-catch blocks with exceptions being manually logged to ELMAH.
However, there is one case that has me stumped. If malformed Json is posted to the WebMethod URL, it throws an exception before entering our code, and I can't find any way to trap this.
e.g. this WebMethod signature
[WebMethod]
public static string LeWebMethod(string stringParam, int intParam)
Normally called with a Json payload like:
{"stringParam":"oh hai","intParam":37}
I tried a test using Fiddler to edit the payload to the malformed Json:
{"stringParam":"oh hai","intPara
And got the following ArgumentException
error response from JavaScriptObjectDeserializer
sent to the client (this is in a simple test app running locally with no custom errors):
{"Message":"Unterminated string passed in. (32): {\"stringParam\":\"oh hai\",\"intPara","StackTrace":" at
System.Web.Script.Serialization.JavaScriptObjectDeserializer.DeserializeString()\r\n at
System.Web.Script.Serialization.JavaScriptObjectDeserializer.DeserializeMemberName()\r\n at
System.Web.Script.Serialization.JavaScriptObjectDeserializer.DeserializeDictionary(Int32 depth)\r\n at
System.Web.Script.Serialization.JavaScriptObjectDeserializer.DeserializeInternal(Int32 depth)\r\n at
System.Web.Script.Serialization.JavaScriptObjectDeserializer.BasicDeserialize(String input, Int32 depthLimit, JavaScriptSerializer serializer)\r\n at
System.Web.Script.Serialization.JavaScriptSerializer.Deserialize(JavaScriptSerializer serializer, String input, Type type, Int32 depthLimit)\r\n at
System.Web.Script.Serialization.JavaScriptSerializer.Deserialize[T](String input)\r\n at
System.Web.Script.Services.RestHandler.GetRawParamsFromPostRequest(HttpContext context, JavaScriptSerializer serializer)\r\n at
System.Web.Script.Services.RestHandler.GetRawParams(WebServiceMethodData methodData, HttpContext context)\r\n at
System.Web.Script.Services.RestHandler.ExecuteWebServiceCall(HttpContext context, WebServiceMethodData methodData)","ExceptionType":"System.ArgumentException"}
It's still not firing the Application_Error
event, and it never enters our code so we can't log the error ourselves.
I found a similar question which got a pointer to the blog post "How to create a global exception handler for a Web Service" but that appears to only be valid for SOAP webservices, not AJAX GETs/POSTs.
Is there some similar way to attach a custom handler in my situation?
When you say that you have static methods on the page code-behind marked with
WebMethod
and you say that you use$.ajax
, that sounds just wrong. But I'll give the benefit of the doubt, as I don't know the particularities of you system.Anyway, please test this:
You should have a ScriptManager on your page looking like this: (**1)
Then in that place where you have your
$.ajax
call, call you Page Method like this: (**2)(**1)
(**2)
Know using ASP.NET Ajax Library the proper way, give it a test, and see if the error reports back to you properly.
P.S: Sorry for the bookmark style notation, but SO, seems be experiencing some malfunction right now.
UPDATE
Reading this post, seems to explain the problem you are facing:
(...) If the request is for a class that implements System.Web.UI.Page and it is a rest method call, the WebServiceData class (that was explained in a previous post) is used to call the requested method from the Page. After the method has been called, the CompleteRequest method is called, bypassing all pipeline events and executing the EndRequest method. This allows MS AJAX to be able to call a method on a page instead of having to create a web service to call a method. (...)
Try to use the ASP.NET JavaScript Proxies, to check if you can capture the error using Microsoft Generated Code.
Here is a solution that replaces the internal RestHandler implementation with my own version. You can log the exception in the WriteExceptionJsonString method. This uses an answer provided on Dynamically replace the contents of a C# method? to swap out the method. I've confirmed it works for me if I add a call to ReplaceRestHandler in my Global.asax Application_Start method. Haven't run this very long or in production so use at your own risk.
According to the reference source, the internal
RestHandler.ExecuteWebServiceCall
method catches all exceptions thrown byGetRawParams
and simply writes them to the response stream, which is whyApplication_Error
isn't invoked:The only workaround I can think of is to create an output filter that intercepts and logs the output:
In Global.asax.cs (or in an HTTP module), install the filter in
Application_PostMapRequestHandler
:These links might help you to handle the error on the client side,
stackoverflow
unseenrevolution
asp.net
encosia
then you could trigger a control event from client side to pass the error through the server and do the logging.
This article suggests that there are two ways to extend WebMethods of which the SoapExtension is the easier. This other one shows an example how to write a SoapExtension. It looks like the place where you can do message validation.