MVC 5 Increase Max JSON Length in POST Request
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