8

I am sending a POST request to an MVC controller with a large amount of JSON data in the body and it is throwing the following:

ArgumentException: Error during serialization or deserialization using the JSON JavaScriptSerializer. The length of the string exceeds the value set on the maxJsonLength property. Parameter name: input

To remedy this, I have tried a number of Web.Config solutions. Namely:

<system.web> 
...
<httpRuntime maxRequestLength="2147483647" />
</system.web>

...

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

Now, the controller that I am communicating with is in its own Area, with its own Web.Config. I have tried placing the above in either the root or the Area's Web.Config individually, but neither are working. When I debug on a different method within the same controller, I get the default JSON Max Length with the following:

Console.WriteLine(new ScriptingJsonSerializationSection().MaxJsonLength);
// 102400

Here is the method that I am wanting to POST against:

[HttpPost]
public JsonResult MyMethod (string data = "") { //... }

How can I increase the Max JSON Length for the MVC Controller so that my request can successfully reach the method?

Edit: Added <httpRuntime maxRequestLength="2147483647" />

Alireza Ahmadi
  • 8,579
  • 5
  • 15
  • 42
treeblah
  • 1,205
  • 2
  • 11
  • 26
  • Did you try to increase the response length in web config. Reference http://stackoverflow.com/questions/16436533/how-to-modify-the-default-allowed-response-size-settings-for-a-web-api-applicati – Saravanan Dec 15 '16 at 15:32
  • Yeah, I have `` within my Web.Config as well. – treeblah Dec 15 '16 at 15:35
  • Can you not stream the response if it's too big? – Saravanan Dec 15 '16 at 15:38
  • Do you mean fetch the JSON from the request stream rather than use a parameter on the method? – treeblah Dec 15 '16 at 15:40
  • 1
    @aiokos The only other solution that you commonly see in these cases, other than the maxJsonLength and the maxRequestLength is `` in your web.config's `appSettings` section. Try that out. – KreepN Dec 15 '16 at 15:44
  • try this in web.config – rashfmnb Dec 15 '16 at 15:49
  • No luck with the `httpRuntime` or the `key="aspnet:MaxJsonDeserializerMembers"`, unfortunately. – treeblah Dec 15 '16 at 15:51
  • What size is the JSON you're returning? – ColinM Dec 15 '16 at 20:10
  • Also, excuse the double-comment, see the following answers. http://stackoverflow.com/questions/1151987/can-i-set-an-unlimited-length-for-maxjsonlength-in-web-config/7207539#7207539 this answer also http://stackoverflow.com/a/12278956/5062791. – ColinM Dec 15 '16 at 20:23
  • 7
    It's not about the size of the JSON being returned, since the Exception is thrown before the body of my method is even called. The problem is the default MVC model binder's JSON max length. The JSON that I am sending in the POST body is about 2mb. – treeblah Dec 15 '16 at 22:18

3 Answers3

20

So, although this is a rather disagreeable solution, I got around the problem by reading the request stream manually, rather than relying on MVC's model binders.

For example, my method

[HttpPost]
public JsonResult MyMethod (string data = "") { //... }

Became

[HttpPost]
public JsonResult MyMethod () {
    Stream req = Request.InputStream;
    req.Seek(0, System.IO.SeekOrigin.Begin);
    string json = new StreamReader(req).ReadToEnd();
    MyModel model = JsonConvert.DeserializeObject<MyModel>(json);
    // use model...
}

This way I could use JSON.NET and get around the JSON max length restrictions with MVC's default deserializer.

To adapt this solution, I would recommend creating a custom JsonResult factory that will replace the old in Application_Start().

treeblah
  • 1,205
  • 2
  • 11
  • 26
  • I have a rather large JSON string that send in 22 different transactions. I set them as pages in my json formatting. You could try that if the solution above doesn't set well with you. – Tyriddik Dec 15 '16 at 21:30
  • That's a good idea. I think I'll stick with this solution for now until I override the default MVC JSON serializer with JSON.NET's. It's super unlikely that a request of this size would occur in the first place. – treeblah Dec 15 '16 at 22:20
  • I was about ready to switch everything to another technology, scraping the use of angular-base64-upload, until I found this work around as none of the web.config size settings had any effect. Thanks!. – RandallTo Dec 27 '17 at 17:36
  • I love this hack. Tested all of the above web.config settings but nothing helped. This made the day! – gurkan Sep 24 '19 at 13:01
  • Same as @gurkan above -- I tried everything else and this was the only thing that worked. – Kyle Whittington Jan 09 '20 at 15:16
  • Still no good solution for this in MVC 5 perhaps there is in .Net Core, but you can now use `Request.Form` and `Request.Form.Get("")` to get the posted data instead of using the stream directly. Note: this form property works with both posted forms and ajax post calls. – Matt G Jun 15 '20 at 21:36
2

Problem:

The problem is in JsonValueProviderFactory class in System.Web.Mvc namespace. Actually if you decompile System.Web.Mvc.dll and find the JsonValueProviderFactory class you will see that in GetDeserializedObject methods it has used JavaScriptSerializer without setting any value for MaxJsonLength:

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;
    }
    // The problem is here, not given. javaScriptSerializer.MaxJsonLength The default value is 2097152 bytes, that is 2. M
    JavaScriptSerializer javaScriptSerializer = new JavaScriptSerializer();
    return javaScriptSerializer.DeserializeObject(text);
}

Solution: You can rewrite the JsonValueProviderFactory class and set javaScriptSerializer.MaxJsonLength and then replace this class in Application_Start() methods in Global.asax like this:

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

Here is full working code:

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();
            // To solve this problem:
            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;
        }
    }
}

References: https://www.fatalerrors.org/a/net-mvc-json-javascriptserializer-string-exceeds-the-maxjsonlength.html

Alireza Ahmadi
  • 8,579
  • 5
  • 15
  • 42
-1
<system.web>
    <httpRuntime  maxRequestLength="1048576" />  
</system.web>

<system.webServer>
    <security>
      <requestFiltering>
        <requestLimits maxAllowedContentLength="1073741824" />
      </requestFiltering>
    </security>
</system.webServer>