Catching errors from calling ASP.NET WebMethod with malformed Json Catching errors from calling ASP.NET WebMethod with malformed Json asp.net asp.net

Catching errors from calling ASP.NET WebMethod with malformed Json


According to the reference source, the internal RestHandler.ExecuteWebServiceCall method catches all exceptions thrown by GetRawParams and simply writes them to the response stream, which is why Application_Error isn't invoked:

internal static void ExecuteWebServiceCall(HttpContext context, WebServiceMethodData methodData) {    try {        ...        IDictionary<string, object> rawParams = GetRawParams(methodData, context);        InvokeMethod(context, methodData, rawParams);    }    catch (Exception ex) {        WriteExceptionJsonString(context, ex);    }}

The only workaround I can think of is to create an output filter that intercepts and logs the output:

public class PageMethodExceptionLogger : Stream{    private readonly HttpResponse _response;    private readonly Stream _baseStream;    private readonly MemoryStream _capturedStream = new MemoryStream();    public PageMethodExceptionLogger(HttpResponse response)    {        _response = response;        _baseStream = response.Filter;    }    public override void Close()    {        if (_response.StatusCode == 500 && _response.Headers["jsonerror"] == "true")        {            _capturedStream.Position = 0;            string responseJson = new StreamReader(_capturedStream).ReadToEnd();            // TODO: Do the actual logging.        }        _baseStream.Close();        base.Close();    }    public override void Flush()    {        _baseStream.Flush();    }    public override long Seek(long offset, SeekOrigin origin)    {        return _baseStream.Seek(offset, origin);    }    public override void SetLength(long value)    {        _baseStream.SetLength(value);    }    public override int Read(byte[] buffer, int offset, int count)    {        return _baseStream.Read(buffer, offset, count);    }    public override void Write(byte[] buffer, int offset, int count)    {        _baseStream.Write(buffer, offset, count);        _capturedStream.Write(buffer, offset, count);    }    public override bool CanRead { get { return _baseStream.CanRead; } }    public override bool CanSeek { get { return _baseStream.CanSeek; } }    public override bool CanWrite { get { return _baseStream.CanWrite; } }    public override long Length { get { return _baseStream.Length; } }    public override long Position    {        get { return _baseStream.Position; }        set { _baseStream.Position = value; }    }}

In Global.asax.cs (or in an HTTP module), install the filter in Application_PostMapRequestHandler:

protected void Application_PostMapRequestHandler(object sender, EventArgs e){    HttpContext context = HttpContext.Current;    if (context.Handler is Page && !string.IsNullOrEmpty(context.Request.PathInfo))    {        string contentType = context.Request.ContentType.Split(';')[0];        if (contentType.Equals("application/json", StringComparison.OrdinalIgnoreCase))        {            context.Response.Filter = new PageMethodExceptionLogger(context.Response);        }    }}


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.


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)

<asp:ScriptManager ID="smPageManager"        runat="server"        EnablePageMethods="true"         ScriptMode="Release"         LoadScriptsBeforeUI="true"> </asp:ScriptManager>

(**2)

PageMethods.LeWebMethod("hero", 1024, function(response){    alert(response);}, function(error){    alert(error);});

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.