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" />
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()
.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With