Intro:
Web application, ASP.NET MVC 3, a controller action that accepts an instance of POCO model class with (potentially) large field.
Model class:
public class View { [Required] [RegularExpression(...)] public object name { get; set; } public object details { get; set; } public object content { get; set; } // the problem field }
Controller action:
[ActionName(...)] [Authorize(...)] [HttpPost] public ActionResult CreateView(View view) { if (!ModelState.IsValid) { return /*some ActionResult here*/;} ... //do other stuff, create object in db etc. return valid result }
Problem:
An action should be able to accept large JSON objects (at least up to hundred megabytes in a single request and that's no joke). By default I met with several restrictions like httpRuntime maxRequestLength
etc. - all solved except MaxJsonLengh - meaning that default ValueProviderFactory for JSON is not capable of handling such objects.
Tried:
Setting
<system.web.extensions> <scripting> <webServices> <jsonSerialization maxJsonLength="2147483647"/> </webServices> </scripting> </system.web.extensions>
Creating my own custom ValueProviderFactory as described in @Darin's answer here:
JsonValueProviderFactory throws "request too large"
Dictionary<String,Object>
here, but that's not what I want - I want to deserialize it to my lovely POCO objects and use them as input parameters for actions.So, the questions:
Anyone can suggest a good solution?
Property Value The maximum length of JSON strings. The default is 2097152 characters, which is equivalent to 4 MB of Unicode string data.
SerializeObject Method (Object, Type, JsonSerializerSettings) Serializes the specified object to a JSON string using a type, formatting and JsonSerializerSettings.
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());
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