在调用格式不正确的Json的ASP.NET WebMethod时捕获错误
在调用格式不正确的Json的ASP.NET WebMethod时捕获错误
我们有一个旧的ASP.NET WebForms应用程序,通过在客户端使用jQuery $.ajax()
调用来执行AJAX请求,调用带有[WebMethod]
属性修饰的页面代码后端中的静态方法。
如果WebMethod内发生未处理的异常,则不会触发Application_Error
事件,因此不会由我们的错误记录器(ELMAH)捕获。这是众所周知的,没有问题-我们将所有WebMethod代码包装在try-catch块中,异常被手动记录到ELMAH中。
然而,有一个案例让我束手无策。如果向WebMethod URL发布格式不正确的Json,则它会在进入我们的代码之前抛出异常,我找不到任何方法来捕获它。
例如,这个WebMethod签名
[WebMethod] public static string LeWebMethod(string stringParam, int intParam)
通常用以下Json有效载荷调用:
{"stringParam":"oh hai","intParam":37}
我试图使用Fiddler进行测试,将有效载荷编辑为格式不正确的Json:
{"stringParam":"oh hai","intPara
然后从JavaScriptObjectDeserializer
得到以下ArgumentException
错误响应发送到客户端(这是在本地运行的简单测试应用程序,没有自定义错误):
{"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
事件,也没有进入我们的代码,因此我们无法自己记录错误。
我找到了一个类似的问题,它得到了指向博客文章“如何为Web服务创建全局异常处理程序”的指针,但这似乎只适用于SOAP Web服务,而不适用于AJAX GET/POST。
在我的情况下,有没有一种类似的方法可以附加自定义处理程序?
根据参考来源,内部的RestHandler.ExecuteWebServiceCall
方法捕获所有由GetRawParams
抛出的异常并将其简单地写入响应流中,这就是为什么Application_Error
不会被调用的原因:
\n
internal static void ExecuteWebServiceCall(HttpContext context, WebServiceMethodData methodData) { try { ... IDictionaryrawParams = GetRawParams(methodData, context); InvokeMethod(context, methodData, rawParams); } catch (Exception ex) { WriteExceptionJsonString(context, ex); } }
\n我唯一能想到的解决方法是创建一个输出过滤器来拦截并记录输出:
\n
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; } } }
\n在Global.asax.cs(或HTTP模块中),在Application_PostMapRequestHandler
中安装过滤器:
\n
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); } } }