JsonMaxLength exception on deserializing large json objects

前端 未结 3 2057
栀梦
栀梦 2020-12-23 02:10

Intro:

Web application, ASP.NET MVC 3, a controller action that accepts an instance of POCO model class with (potentially) large field.

Mode

相关标签:
3条回答
  • 2020-12-23 02:47

    I found that the maxRequestLength did not solve the problem however. I resolved my issue with the below setting. It is cleaner than having to implement a custom ValueProviderFactory

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

    Credit goes to the following questions:

    JsonValueProviderFactory throws "request too large"

    Getting "The JSON request was too large to be deserialized"

    This setting obviously relates to a highly complex json model and not the actual size.

    0 讨论(0)
  • 2020-12-23 02:51

    The built-in JsonValueProviderFactory ignores the <jsonSerialization maxJsonLength="50000000"/> setting. So you could write a custom factory by using the built-in implementation:

    public sealed class MyJsonValueProviderFactory : ValueProviderFactory
    {
        private static void AddToBackingStore(Dictionary<string, object> backingStore, string prefix, object value)
        {
            IDictionary<string, object> d = value as IDictionary<string, object>;
            if (d != null)
            {
                foreach (KeyValuePair<string, object> entry in d)
                {
                    AddToBackingStore(backingStore, MakePropertyKey(prefix, entry.Key), entry.Value);
                }
                return;
            }
    
            IList l = value as IList;
            if (l != null)
            {
                for (int i = 0; i < l.Count; i++)
                {
                    AddToBackingStore(backingStore, MakeArrayKey(prefix, i), l[i]);
                }
                return;
            }
    
            // primitive
            backingStore[prefix] = value;
        }
    
        private static object GetDeserializedObject(ControllerContext controllerContext)
        {
            if (!controllerContext.HttpContext.Request.ContentType.StartsWith("application/json", StringComparison.OrdinalIgnoreCase))
            {
                // not JSON request
                return null;
            }
    
            StreamReader reader = new StreamReader(controllerContext.HttpContext.Request.InputStream);
            string bodyText = reader.ReadToEnd();
            if (String.IsNullOrEmpty(bodyText))
            {
                // no JSON data
                return null;
            }
    
            JavaScriptSerializer serializer = new JavaScriptSerializer();
            serializer.MaxJsonLength = 2147483647;
            object jsonData = serializer.DeserializeObject(bodyText);
            return jsonData;
        }
    
        public override IValueProvider GetValueProvider(ControllerContext controllerContext)
        {
            if (controllerContext == null)
            {
                throw new ArgumentNullException("controllerContext");
            }
    
            object jsonData = GetDeserializedObject(controllerContext);
            if (jsonData == null)
            {
                return null;
            }
    
            Dictionary<string, object> backingStore = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
            AddToBackingStore(backingStore, String.Empty, jsonData);
            return new DictionaryValueProvider<object>(backingStore, CultureInfo.CurrentCulture);
        }
    
        private static string MakeArrayKey(string prefix, int index)
        {
            return prefix + "[" + index.ToString(CultureInfo.InvariantCulture) + "]";
        }
    
        private static string MakePropertyKey(string prefix, string propertyName)
        {
            return (String.IsNullOrEmpty(prefix)) ? propertyName : prefix + "." + propertyName;
        }
    }
    

    The only modification I did compared to the default factory is adding the following line:

    serializer.MaxJsonLength = 2147483647;
    

    Unfortunately this factory is not extensible at all, sealed stuff so I had to recreate it.

    and in your Application_Start:

    ValueProviderFactories.Factories.Remove(ValueProviderFactories.Factories.OfType<System.Web.Mvc.JsonValueProviderFactory>().FirstOrDefault());
    ValueProviderFactories.Factories.Add(new MyJsonValueProviderFactory());
    
    0 讨论(0)
  • The solution of Darin Dimitrov works for me but i need reset the position of the stream of the request before read it, adding this line:

    controllerContext.HttpContext.Request.InputStream.Position = 0;

    So now, the method GetDeserializedObject looks like this:

     private static object GetDeserializedObject(ControllerContext controllerContext)
        {
            if (!controllerContext.HttpContext.Request.ContentType.StartsWith("application/json", StringComparison.OrdinalIgnoreCase))
            {
                // not JSON request
                return null;
            }
            controllerContext.HttpContext.Request.InputStream.Position = 0;
            StreamReader reader = new StreamReader(controllerContext.HttpContext.Request.InputStream);
            string bodyText = reader.ReadToEnd();
            if (String.IsNullOrEmpty(bodyText))
            {
                // no JSON data
                return null;
            }
    
            JavaScriptSerializer serializer = new JavaScriptSerializer();
            serializer.MaxJsonLength = 2147483647;
            object jsonData = serializer.DeserializeObject(bodyText);
            return jsonData;
        }
    
    0 讨论(0)
提交回复
热议问题