欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

.NET MVC JSON JavaScriptSerializer 字符串的长度超过 maxJsonLength 值问题的解决

程序员文章站 2022-06-24 08:37:47
.NET MVC JSON JavaScriptSerializer 字符串的长度超过 maxJsonLength 值问题的解决 ......
[ArgumentException: 使用 JSON JavaScriptSerializer 序列化或还原序列化期间发生错误。字符串的长度超过在 maxJsonLength 属性上设定的值。
参数名称: input]
   System.Web.Script.Serialization.JavaScriptSerializer.Deserialize(JavaScriptSerializer serializer, String input, Type type, Int32 depthLimit) +168
   System.Web.Mvc.JsonValueProviderFactory.GetDeserializedObject(ControllerContext controllerContext) +213
   System.Web.Mvc.JsonValueProviderFactory.GetValueProvider(ControllerContext controllerContext) +16
   System.Web.Mvc.ValueProviderFactoryCollection.GetValueProvider(ControllerContext controllerContext) +69
   System.Web.Mvc.ControllerBase.get_ValueProvider() +30  

由于前端 Post 到 Action 的参数太大,超过了2M,还没进入后台的 Action 方法就报错了。这个问题困扰了很久,一直未解决。网上找了几个方法都无效。

在 web.config 中加入这些,没有作用:

<appSettings>
  <add key="aspnet:MaxJsonDeserializerMembers" value="2147483647" />
  <add key="aspnet:UpdatePanelMaxScriptLength" value="2147483647" />
</appSettings>

在 web.config 中加入这些,也没有作用:

<system.web.extensions>
  <scripting>
    <webServices>
      <jsonSerialization maxJsonLength="2147483647">
      </jsonSerialization>
    </webServices>
  </scripting>
</system.web.extensions>

仔细看了一下异常信息,发现,是在System.Web.Mvc.JsonValueProviderFactory 里调用的 JavaScriptSerializer:

.NET MVC JSON JavaScriptSerializer 字符串的长度超过 maxJsonLength 值问题的解决

于是查了一下 ,发现 JsonValueProviderFactory 在 System.Web.Mvc.dll 程序集里的:

.NET MVC JSON JavaScriptSerializer 字符串的长度超过 maxJsonLength 值问题的解决

反编译 System.Web.Mvc.dll 找到 JsonValueProviderFactory 类:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Configuration;
using System.Globalization;
using System.IO;
using System.Web.Mvc.Properties;
using System.Web.Script.Serialization;
namespace System.Web.Mvc
{
	public sealed class JsonValueProviderFactory : ValueProviderFactory
	{
		private class EntryLimitedDictionary
		{
			private static int _maximumDepth = JsonValueProviderFactory.EntryLimitedDictionary.GetMaximumDepth();
			private readonly IDictionary<string, object> _innerDictionary;
			private int _itemCount;
			public EntryLimitedDictionary(IDictionary<string, object> innerDictionary)
			{
				this._innerDictionary = innerDictionary;
			}
			public void Add(string key, object value)
			{
				if (++this._itemCount > JsonValueProviderFactory.EntryLimitedDictionary._maximumDepth)
				{
					throw new InvalidOperationException(MvcResources.JsonValueProviderFactory_RequestTooLarge);
				}
				this._innerDictionary.Add(key, value);
			}
			private static int GetMaximumDepth()
			{
				NameValueCollection appSettings = ConfigurationManager.AppSettings;
				if (appSettings != null)
				{
					string[] values = appSettings.GetValues("aspnet:MaxJsonDeserializerMembers");
					int result;
					if (values != null && values.Length > 0 && int.TryParse(values[0], out result))
					{
						return result;
					}
				}
				return 1000;
			}
		}
		private static void AddToBackingStore(JsonValueProviderFactory.EntryLimitedDictionary backingStore, string prefix, object value)
		{
			IDictionary<string, object> dictionary = value as IDictionary<string, object>;
			if (dictionary != null)
			{
				foreach (KeyValuePair<string, object> current in dictionary)
				{
					JsonValueProviderFactory.AddToBackingStore(backingStore, JsonValueProviderFactory.MakePropertyKey(prefix, current.Key), current.Value);
				}
				return;
			}
			IList list = value as IList;
			if (list != null)
			{
				for (int i = 0; i < list.Count; i++)
				{
					JsonValueProviderFactory.AddToBackingStore(backingStore, JsonValueProviderFactory.MakeArrayKey(prefix, i), list[i]);
				}
				return;
			}
			backingStore.Add(prefix, value);
		}
		private static object GetDeserializedObject(ControllerContext controllerContext)
		{
			if (!controllerContext.HttpContext.Request.ContentType.StartsWith("application/json", StringComparison.OrdinalIgnoreCase))
			{
				return null;
			}
			StreamReader streamReader = new StreamReader(controllerContext.HttpContext.Request.InputStream);
			string text = streamReader.ReadToEnd();
			if (string.IsNullOrEmpty(text))
			{
				return null;
			}
			// 问题就出在这里,没有给 javaScriptSerializer.MaxJsonLength 赋值,其默认值是 2097152 字节,即2M
			JavaScriptSerializer javaScriptSerializer = new JavaScriptSerializer();
			return javaScriptSerializer.DeserializeObject(text);
		}
		public override IValueProvider GetValueProvider(ControllerContext controllerContext)
		{
			if (controllerContext == null)
			{
				throw new ArgumentNullException("controllerContext");
			}
			object deserializedObject = JsonValueProviderFactory.GetDeserializedObject(controllerContext);
			if (deserializedObject == null)
			{
				return null;
			}
			Dictionary<string, object> dictionary = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
			JsonValueProviderFactory.EntryLimitedDictionary backingStore = new JsonValueProviderFactory.EntryLimitedDictionary(dictionary);
			JsonValueProviderFactory.AddToBackingStore(backingStore, string.Empty, deserializedObject);
			return new DictionaryValueProvider<object>(dictionary, CultureInfo.CurrentCulture);
		}
		private static string MakeArrayKey(string prefix, int index)
		{
			return prefix + "[" + index.ToString(CultureInfo.InvariantCulture) + "]";
		}
		private static string MakePropertyKey(string prefix, string propertyName)
		{
			if (!string.IsNullOrEmpty(prefix))
			{
				return prefix + "." + propertyName;
			}
			return propertyName;
		}
	}
}

在 JavaScriptSerializer 没有设置 MaxJsonLength,默认值是 2097152 字节,即2M。 

解决此问题的方法就是 把 javaScriptSerializer.MaxJsonLength = int.MaxValue; (int.MaxValue 值是 2147483647 字节,即2048M)

自己重写类 JsonValueProviderFactory 命名为 MyJsonValueProviderFactory:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Configuration;
using System.Globalization;
using System.IO;
using System.Web.Mvc;
using System.Web.Mvc.Properties;
using System.Web.Script.Serialization;
namespace XXX
{
    public sealed class MyJsonValueProviderFactory : ValueProviderFactory
    {
        private class EntryLimitedDictionary
        {
            private static int _maximumDepth = GetMaximumDepth();
            private readonly IDictionary<string, object> _innerDictionary;
            private int _itemCount;

            public EntryLimitedDictionary(IDictionary<string, object> innerDictionary)
            {
                this._innerDictionary = innerDictionary;
            }

            public void Add(string key, object value)
            {
                if (++this._itemCount > _maximumDepth)
                {
                    //throw new InvalidOperationException(MvcResources.JsonValueProviderFactory_RequestTooLarge);
                    throw new InvalidOperationException("itemCount is over maximumDepth");
                }
                this._innerDictionary.Add(key, value);
            }

            private static int GetMaximumDepth()
            {
                NameValueCollection appSettings = ConfigurationManager.AppSettings;
                if (appSettings != null)
                {
                    string[] values = appSettings.GetValues("aspnet:MaxJsonDeserializerMembers");
                    int result;
                    if (values != null && values.Length > 0 && int.TryParse(values[0], out result))
                    {
                        return result;
                    }
                }
                return 1000;
            }
        }

        private static void AddToBackingStore(EntryLimitedDictionary backingStore, string prefix, object value)
        {
            IDictionary<string, object> dictionary = value as IDictionary<string, object>;
            if (dictionary != null)
            {
                foreach (KeyValuePair<string, object> current in dictionary)
                {
                    AddToBackingStore(backingStore, MakePropertyKey(prefix, current.Key), current.Value);
                }
                return;
            }
            IList list = value as IList;
            if (list != null)
            {
                for (int i = 0; i < list.Count; i++)
                {
                    AddToBackingStore(backingStore, MakeArrayKey(prefix, i), list[i]);
                }
                return;
            }
            backingStore.Add(prefix, value);
        }

        private static object GetDeserializedObject(ControllerContext controllerContext)
        {
            if (!controllerContext.HttpContext.Request.ContentType.StartsWith("application/json", StringComparison.OrdinalIgnoreCase))
            {
                return null;
            }
            StreamReader streamReader = new StreamReader(controllerContext.HttpContext.Request.InputStream);
            string text = streamReader.ReadToEnd();
            if (string.IsNullOrEmpty(text))
            {
                return null;
            }
            JavaScriptSerializer javaScriptSerializer = new JavaScriptSerializer();
            // 解决这个问题:
            // 使用 JSON JavaScriptSerializer 序列化或还原序列化期间发生错误。字符串的长度超过在 maxJsonLength 属性上设定的值。
            javaScriptSerializer.MaxJsonLength = int.MaxValue;
            // ----------------------------------------
            return javaScriptSerializer.DeserializeObject(text);
        }

        public override IValueProvider GetValueProvider(ControllerContext controllerContext)
        {
            if (controllerContext == null)
            {
                throw new ArgumentNullException("controllerContext");
            }
            object deserializedObject = GetDeserializedObject(controllerContext);
            if (deserializedObject == null)
            {
                return null;
            }
            Dictionary<string, object> dictionary = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
            EntryLimitedDictionary backingStore = new EntryLimitedDictionary(dictionary);
            AddToBackingStore(backingStore, string.Empty, deserializedObject);
            return new DictionaryValueProvider<object>(dictionary, CultureInfo.CurrentCulture);
        }

        private static string MakeArrayKey(string prefix, int index)
        {
            return prefix + "[" + index.ToString(CultureInfo.InvariantCulture) + "]";
        }

        private static string MakePropertyKey(string prefix, string propertyName)
        {
            if (!string.IsNullOrEmpty(prefix))
            {
                return prefix + "." + propertyName;
            }
            return propertyName;
        }
    }
}

然后在 Global.asax 中的 Application_Start() 方法里,加入如下代码,用 MyJsonValueProviderFactory 类代替 System.Web.Mvc.dll 程序集中的 JsonValueProviderFactory 类。

ValueProviderFactories.Factories.Remove(ValueProviderFactories.Factories.OfType<JsonValueProviderFactory>().FirstOrDefault());
ValueProviderFactories.Factories.Add(new MyJsonValueProviderFactory());

 至此,.NET MVC 超出 maxJsonLength 的问题终于解决了!