通过格式错误的Json调用ASP.NET WebMethod捕获错误 [英] Catching errors from calling ASP.NET WebMethod with malformed Json

查看:82
本文介绍了通过格式错误的Json调用ASP.NET WebMethod捕获错误的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我们有一个较旧的ASP.NET WebForms应用程序,该应用程序通过在客户端使用jQuery $。ajax()调用来执行AJAX请求,并在页面代码中调用静态方法-用 [WebMethod] 属性修饰。

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.

如果WebMethod中发生未处理的异常,则不会触发 Application_Error 事件,因此我们的错误记录器未记录该事件( ELMAH )。这是众所周知的,不是问题-我们将所有WebMethod代码包装在try-catch块中,但例外情况会手动记录到ELMAH中。

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.

但是,有一种情况是让我难过了如果格式错误的Json发布到了WebMethod URL,它会在输入我们的代码之前引发异常,而我找不到任何方法来捕获它。

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.

例如此WebMethod签名

e.g. this WebMethod signature

[WebMethod]
public static string LeWebMethod(string stringParam, int intParam)

通常使用Json有效负载调用,例如:

Normally called with a Json payload like:

{"stringParam":"oh hai","intParam":37}

我尝试使用Fiddler进行测试以将有效载荷编辑为格式错误的Json:

I tried a test using Fiddler to edit the payload to the malformed Json:

{"stringParam":"oh hai","intPara

并收到以下 ArgumentException 错误从 JavaScriptObjectDeserializer 发送到客户端的响应(这是在本地运行的简单测试应用中,没有自定义错误):

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"}

它仍然没有触发 Application_Error 事件,它永远不会输入我们的代码,因此我们无法自己记录错误。

It's still not firing the Application_Error event, and it never enters our code so we can't log the error ourselves.

我发现了一个类似的问题,它指向博客文章 如何为Web创建全局异常处理程序服务,但这似乎仅对SOAP Web服务有效,而对AJAX GET / POST无效。

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?

推荐答案

根据参考源,内部 RestHandler.ExecuteWebServiceCall 方法捕获由引发的所有异常GetRawParams nd只是将它们写入响应流,这就是为什么未调用 Application_Error 的原因:

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; }
    }
}

在Global.asax.cs中(或在HTTP模块),将过滤器安装在 Application_PostMapRequestHandler

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);
        }
    }
}

这篇关于通过格式错误的Json调用ASP.NET WebMethod捕获错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆