一个ASP.NET MVC中ajax调用WebApi返回500 Internal Server Error的调错方法。

         ASP.NET MVC 引入的WebApi自然且较好地满足了ajax的交互需求,但使用jQuery ajax调用WebApi返回500 Internal Server Error时却不太好查找错误。在一个实际项目中,WebApi方法全部使用了try-catch捕获异常,并返回定制的错误消息,想当然认为有错误就能捕获。但最近网站运行时却总是有500错误,花了一番功夫才发现问题所在。

        经过多次调试,确定这个错误应该是WebApi方法之外引发的(方法内部应该被try-catch捕获)。初步判断,是在ASP.NET MVC框架的JSON序列化返回对象时引发的一个运行时错误。于是,使用Newtonsoft.Json.JsonConvert.SerializeObject()方法序列化待返回的对象就发现了错误所在:一个类对象的get属性(派生值)中出现被0除的情况。一般而言,对象的get属性仅仅在访问该属性时才运行其中的代码。显然,JSON序列化对象时调用了对象所有的get属性代码并获得持久属性值。

       结论:

  1. WebApi在返回JSON数据时的序列化操作产生的异常,是WebApi方法之外的异常,暂时不能被捕获(.NET Framework 4.0下笔者暂时没有找到捕获的方法),此时将返回500 Internal Server Error错误。
  2. JSON序列化对象时,将获取该对象的所有的get属性值(即运行get属性的代码)。
  3. 可以编程模拟JSON的序列化操作,从而直接抛出并捕获该异常。

      本文介绍的方法在Visual Studio Community 2015、 .NET 4.0和ASP.NET MVC4上调试通过。

        后记:

      经过数天的网查调试,找到了一个通用的、捕获WebApi方法Json序列化产生异常的方法,基本思路:定制ASP.NET MVC默认的Json序列化转换器JsonConverter(该类由Newtonsoft动态库提供),在读写序列化流时捕获异常。

      重写的JsonConverter转换器类代码如下:

using System;
using System.Net.Http.Formatting;
using System.Threading.Tasks;
using Newtonsoft.Json;
using System.IO;
using System.Net.Http;
using System.Net;

namespace CSUST.Kyz
{
    public class TJsonConverter : MediaTypeFormatter
    {
        public TJsonConverter()
        {
            SupportedMediaTypes.Add(new System.Net.Http.Headers.MediaTypeHeaderValue("application/json"));
        }

        public override bool CanWriteType(Type type)
        {
            return true;
        }

        public override bool CanReadType(Type type)
        {
            return true;
        }

        public override Task<object> ReadFromStreamAsync(Type type, Stream readStream, HttpContent content, IFormatterLogger formatterLogger)
        {
            var task = Task<object>.Factory.StartNew(() =>
            {
                try
                {
                    string json;
                    using (var sr = new StreamReader(readStream, System.Text.Encoding.UTF8))
                    {
                        json = sr.ReadToEnd();
                        sr.Close();
                    }

                    return JsonConvert.DeserializeObject(json); 
                }
                catch(Exception err)
                {
                    TLogs.Save("read except:" + err.StackTrace);  // 保存异常信息
                    throw err;
                }
            });

            return task;
        }

        public override Task WriteToStreamAsync(Type type, object value, Stream writeStream, HttpContent content, TransportContext transportContext)
        {
            var task = Task.Factory.StartNew(() =>
            {
                try
                {
                    var json = JsonConvert.SerializeObject(value);
                    byte[] buf = System.Text.Encoding.UTF8.GetBytes(json);
                    writeStream.Write(buf, 0, buf.Length);
                    writeStream.Flush();
                }
                catch(Exception err)
                {
                    TLogs.Save("write except:" + err.StackTrace);  // 保存异常信息
                    throw err;
                }
            });

            return task;
        }
    }
}
       还需要在全局配置文件中注册上述定制的Json序列化转换器,代码如下(Global.asax.cs中的函数):

protected void Application_Start()
{        
    AreaRegistration.RegisterAllAreas();

    WebApiConfig.Register(GlobalConfiguration.Configuration);
    FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
    RouteConfig.RegisterRoutes(RouteTable.Routes);

    GlobalConfiguration.Configuration.Formatters.XmlFormatter.SupportedMediaTypes.Clear();  // 清除全部数据格式
    GlobalConfiguration.Configuration.Formatters.Insert(0, new TJsonConverter());  // 只有定义序列化器及Json格式
}


猜你喜欢

转载自blog.csdn.net/hulihui/article/details/60490052
今日推荐