Excepción MaxJsonLength en ASP.NET MVC durante JavaScriptSerializer

122

En una de mis acciones de controlador, estoy devolviendo un muy grande JsonResultpara llenar una cuadrícula.

Recibo la siguiente InvalidOperationExceptionexcepción:

Error durante la serialización o deserialización con JSON JavaScriptSerializer. La longitud de la cadena excede el valor establecido en la propiedad maxJsonLength.

Desafortunadamente, establecer la maxJsonLengthpropiedad en web.configun valor superior no muestra ningún efecto.

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

No quiero volver a pasarlo como una cadena como se menciona en esta respuesta SO.

En mi investigación, me encontré con esta publicación de blog donde se recomienda escribir uno propio ActionResult(por ejemplo LargeJsonResult : JsonResult) para evitar este comportamiento.

¿Es esta la única solución?
¿Es esto un error en ASP.NET MVC?
¿Me estoy perdiendo de algo?

Cualquier ayuda sería muy apreciada.

Martin Buberl
fuente
2
Sus soluciones funcionan en MVC 3.
MatteoSp
1
@Matteo ¿Estás seguro? Esta es una vieja pregunta y no puedo recordar, pero aparentemente la etiqueté como MVC3. Desafortunadamente, no puedo ver la versión / fecha cuando se solucionó / ​​cerró: aspnet.codeplex.com/workitem/3436
Martin Buberl
1
Claro, estoy trabajando con MVC 3 y funciona. Y afortunadamente, porque en MVC 3 no tienes las propiedades "MaxJsonLength" citadas en la respuesta aceptada.
MatteoSp

Respuestas:

228

Parece que esto se ha solucionado en MVC4.

Puedes hacer esto, que funcionó bien para mí:

public ActionResult SomeControllerAction()
{
  var jsonResult = Json(veryLargeCollection, JsonRequestBehavior.AllowGet);
  jsonResult.MaxJsonLength = int.MaxValue;
  return jsonResult;
}
Orion Edwards
fuente
Estoy configurando una cadena json en una propiedad ViewBag.MyJsonString pero obtengo el mismo error en mi vista en tiempo de ejecución en la siguiente línea de JavaScript: var myJsonObj = @ Html.Raw (Json.Encode (ViewBag.MyJsonString));
Faisal Mq
1
Hola @orionedvards, @ GG, @ MartinBuberl Estoy enfrentando el mismo problema de maxJson, pero cuando publico datos en el controlador, ¿Cómo puedo manejar esto? Pasé tanto tiempo buscando sobre esto. Cualquier ayuda estaría agradecida.
katmanco
En mi caso no funcionó porque tuve que configurar MaxJsonLength antes de que json serializara la colección.
César León
En mi caso funciona bien, tuve que implementarlo debido a "IMÁGENES" en la tabla de datos para presentar al usuario final. Sin él, simplemente cuelgue sin ningún mensaje comprensible.
Mauro Candido
33

También puede usar ContentResultcomo se sugiere aquí en lugar de subclasificar JsonResult.

var serializer = new JavaScriptSerializer { MaxJsonLength = Int32.MaxValue, RecursionLimit = 100 };

return new ContentResult()
{
    Content = serializer.Serialize(data),
    ContentType = "application/json",
};
SliverNinja - MSFT
fuente
2
En mi caso, trabajando en una aplicación desechable, esta solución funcionó mejor para mí. guardado implementando jsonresult. ¡Gracias!
Christo
22

No hay necesidad de una clase personalizada. Esto es todo lo que se necesita:

return new JsonResult { Data = Result, MaxJsonLength = Int32.MaxValue };

¿Dónde Resultestán los datos que desea serializar?

Juan
fuente
Error 137 'System.Web.Mvc.JsonResult' no contiene una definición para 'MaxJsonLength'
PUG
Esto funcionó para mí, sin embargo, aún necesitaba agregar: JsonRequestBehavior = JsonRequestBehavior.AllowGet
DubMan
5

Si usa Json.NET para generar la jsoncadena, no necesita establecer el MaxJsonLengthvalor.

return new ContentResult()
{
    Content = Newtonsoft.Json.JsonConvert.SerializeObject(data),
    ContentType = "application/json",
};
AechoLiu
fuente
4

Resolví el problema siguiendo este enlace.

namespace System.Web.Mvc
{
public sealed class JsonDotNetValueProviderFactory : ValueProviderFactory
{
    public override IValueProvider GetValueProvider(ControllerContext controllerContext)
    {
        if (controllerContext == null)
            throw new ArgumentNullException("controllerContext");

        if (!controllerContext.HttpContext.Request.ContentType.StartsWith("application/json", StringComparison.OrdinalIgnoreCase))
            return null;

        var reader = new StreamReader(controllerContext.HttpContext.Request.InputStream);
        var bodyText = reader.ReadToEnd();

        return String.IsNullOrEmpty(bodyText) ? null : new DictionaryValueProvider<object>(JsonConvert.DeserializeObject<ExpandoObject>(bodyText, new ExpandoObjectConverter()), CultureInfo.CurrentCulture);
    }
}

}

protected void Application_Start()
    {
        AreaRegistration.RegisterAllAreas();

        RegisterGlobalFilters(GlobalFilters.Filters);
        RegisterRoutes(RouteTable.Routes);

        //Remove and JsonValueProviderFactory and add JsonDotNetValueProviderFactory
        ValueProviderFactories.Factories.Remove(ValueProviderFactories.Factories.OfType<JsonValueProviderFactory>().FirstOrDefault());
        ValueProviderFactories.Factories.Add(new JsonDotNetValueProviderFactory());
    }
Sajjad Ali Khan
fuente
3

Me sorprende que nadie haya sugerido usar un filtro de resultados. Esta es la forma más limpia de engancharse globalmente a la línea de acción / resultado:

public class JsonResultFilter : IResultFilter
{
    public int? MaxJsonLength { get; set; }

    public int? RecursionLimit { get; set; }

    public void OnResultExecuting(ResultExecutingContext filterContext)
    {
        if (filterContext.Result is JsonResult jsonResult)
        {
            // override properties only if they're not set
            jsonResult.MaxJsonLength = jsonResult.MaxJsonLength ?? MaxJsonLength;
            jsonResult.RecursionLimit = jsonResult.RecursionLimit ?? RecursionLimit;
        }
    }

    public void OnResultExecuted(ResultExecutedContext filterContext)
    {
    }
}

Luego, registre una instancia de esa clase usando GlobalFilters.Filters:

GlobalFilters.Filters.Add(new JsonResultFilter { MaxJsonLength = int.MaxValue });
Ronnie Overby
fuente
2

Puede intentar definir en su expresión LINQ solo los campos que necesitará.

Ejemplo. Imagine que tiene un Modelo con Id, Nombre, Teléfono e Imagen (matriz de bytes) y necesita cargar desde json en una lista de selección.

Consulta LINQ:

var listItems = (from u in Users where u.name.Contains(term) select u).ToList();

El problema aquí es " seleccionar u " que obtiene todos los campos. Entonces, si tienes fotos grandes, booomm.

¿Cómo resolver? Muy, muy simple.

var listItems = (from u in Users where u.name.Contains(term) select new {u.Id, u.Name}).ToList();

Las mejores prácticas son seleccionar solo el campo que usará.

Recuerda. Este es un consejo simple, pero puede ayudar a muchos desarrolladores de ASP.NET MVC.

vidrios
fuente
1
No asumiría que el usuario en este caso quiere filtrar sus datos. Algunas personas tienen requisitos para recuperar una gran cantidad de filas de la base de datos ...
Simon Nicholls
2

Solución alternativa de ASP.NET MVC 5:

En mi caso, el error estaba ocurriendo durante la solicitud. El mejor enfoque en mi escenario es modificar el real JsonValueProviderFactoryque aplica la corrección al proyecto global y puede hacerse editando el global.csarchivo como tal.

JsonValueProviderConfig.Config(ValueProviderFactories.Factories);

agregue una entrada web.config:

<add key="aspnet:MaxJsonLength" value="20971520" />

y luego crea las dos clases siguientes

public class JsonValueProviderConfig
{
    public static void Config(ValueProviderFactoryCollection factories)
    {
        var jsonProviderFactory = factories.OfType<JsonValueProviderFactory>().Single();
        factories.Remove(jsonProviderFactory);
        factories.Add(new CustomJsonValueProviderFactory());
    }
}

Esto es básicamente una copia exacta de la implementación predeterminada que se encuentra en System.Web.Mvcpero con la adición de un valor configurable de configuración web.config aspnet:MaxJsonLength.

public class CustomJsonValueProviderFactory : ValueProviderFactory
{

    /// <summary>Returns a JSON value-provider object for the specified controller context.</summary>
    /// <returns>A JSON value-provider object for the specified controller context.</returns>
    /// <param name="controllerContext">The controller context.</param>
    public override IValueProvider GetValueProvider(ControllerContext controllerContext)
    {
        if (controllerContext == null)
            throw new ArgumentNullException("controllerContext");

        object deserializedObject = CustomJsonValueProviderFactory.GetDeserializedObject(controllerContext);
        if (deserializedObject == null)
            return null;

        Dictionary<string, object> strs = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
        CustomJsonValueProviderFactory.AddToBackingStore(new CustomJsonValueProviderFactory.EntryLimitedDictionary(strs), string.Empty, deserializedObject);

        return new DictionaryValueProvider<object>(strs, CultureInfo.CurrentCulture);
    }

    private static object GetDeserializedObject(ControllerContext controllerContext)
    {
        if (!controllerContext.HttpContext.Request.ContentType.StartsWith("application/json", StringComparison.OrdinalIgnoreCase))
            return null;

        string fullStreamString = (new StreamReader(controllerContext.HttpContext.Request.InputStream)).ReadToEnd();
        if (string.IsNullOrEmpty(fullStreamString))
            return null;

        var serializer = new JavaScriptSerializer()
        {
            MaxJsonLength = CustomJsonValueProviderFactory.GetMaxJsonLength()
        };
        return serializer.DeserializeObject(fullStreamString);
    }

    private static void AddToBackingStore(EntryLimitedDictionary backingStore, string prefix, object value)
    {
        IDictionary<string, object> strs = value as IDictionary<string, object>;
        if (strs != null)
        {
            foreach (KeyValuePair<string, object> keyValuePair in strs)
                CustomJsonValueProviderFactory.AddToBackingStore(backingStore, CustomJsonValueProviderFactory.MakePropertyKey(prefix, keyValuePair.Key), keyValuePair.Value);

            return;
        }

        IList lists = value as IList;
        if (lists == null)
        {
            backingStore.Add(prefix, value);
            return;
        }

        for (int i = 0; i < lists.Count; i++)
        {
            CustomJsonValueProviderFactory.AddToBackingStore(backingStore, CustomJsonValueProviderFactory.MakeArrayKey(prefix, i), lists[i]);
        }
    }

    private class EntryLimitedDictionary
    {
        private static int _maximumDepth;

        private readonly IDictionary<string, object> _innerDictionary;

        private int _itemCount;

        static EntryLimitedDictionary()
        {
            _maximumDepth = CustomJsonValueProviderFactory.GetMaximumDepth();
        }

        public EntryLimitedDictionary(IDictionary<string, object> innerDictionary)
        {
            this._innerDictionary = innerDictionary;
        }

        public void Add(string key, object value)
        {
            int num = this._itemCount + 1;
            this._itemCount = num;
            if (num > _maximumDepth)
            {
                throw new InvalidOperationException("The length of the string exceeds the value set on the maxJsonLength property.");
            }
            this._innerDictionary.Add(key, value);
        }
    }

    private static string MakeArrayKey(string prefix, int index)
    {
        return string.Concat(prefix, "[", index.ToString(CultureInfo.InvariantCulture), "]");
    }

    private static string MakePropertyKey(string prefix, string propertyName)
    {
        if (string.IsNullOrEmpty(prefix))
        {
            return propertyName;
        }
        return string.Concat(prefix, ".", propertyName);
    }

    private static int GetMaximumDepth()
    {
        int num;
        NameValueCollection appSettings = ConfigurationManager.AppSettings;
        if (appSettings != null)
        {
            string[] values = appSettings.GetValues("aspnet:MaxJsonDeserializerMembers");
            if (values != null && values.Length != 0 && int.TryParse(values[0], out num))
            {
                return num;
            }
        }
        return 1000;
    }

    private static int GetMaxJsonLength()
    {
        int num;
        NameValueCollection appSettings = ConfigurationManager.AppSettings;
        if (appSettings != null)
        {
            string[] values = appSettings.GetValues("aspnet:MaxJsonLength");
            if (values != null && values.Length != 0 && int.TryParse(values[0], out num))
            {
                return num;
            }
        }
        return 1000;
    }
}
Maxim Gershkovich
fuente
Muchas gracias !
larizzatg
1

Nada de lo anterior funcionó para mí hasta que cambié la Acción como [HttpPost]. e hizo el tipo ajax como POST.

    [HttpPost]
    public JsonResult GetSelectedSignalData(string signal1,...)
    {
         JsonResult result = new JsonResult();
         var signalData = GetTheData();
         try
         {
              var serializer = new System.Web.Script.Serialization.JavaScriptSerializer { MaxJsonLength = Int32.MaxValue, RecursionLimit = 100 };

            result.Data = serializer.Serialize(signalData);
            return Json(result, JsonRequestBehavior.AllowGet);
            ..
            ..
            ...

    }

Y la llamada ajax como

$.ajax({
    type: "POST",
    url: some_url,
    data: JSON.stringify({  signal1: signal1,.. }),
    contentType: "application/json; charset=utf-8",
    success: function (data) {
        if (data !== null) {
            setValue();
        }

    },
    failure: function (data) {
        $('#errMessage').text("Error...");
    },
    error: function (data) {
        $('#errMessage').text("Error...");
    }
});
jAntoni
fuente
1
    protected override JsonResult Json(object data, string contentType, System.Text.Encoding contentEncoding, JsonRequestBehavior behavior)
    {
        return new JsonResult()
        {
            Data = data,
            ContentType = contentType,
            ContentEncoding = contentEncoding,
            JsonRequestBehavior = behavior,
            MaxJsonLength = Int32.MaxValue
        };
    }

Fue la solución para mí en MVC 4.

eaglei22
fuente
0

Debe leer la sección de configuración manualmente antes de que su código devuelva un objeto JsonResult. Simplemente lea desde web.config en una sola línea:

        var jsonResult = Json(resultsForAjaxUI);
        jsonResult.MaxJsonLength = (ConfigurationManager.GetSection("system.web.extensions/scripting/webServices/jsonSerialization") as System.Web.Configuration.ScriptingJsonSerializationSection).MaxJsonLength;
        return jsonResult;

Asegúrese de haber definido el elemento de configuración en web.config

Pavel Nazarov
fuente
0

esto funcionó para mí

        JsonSerializerSettings json = new JsonSerializerSettings
        {
            ReferenceLoopHandling = ReferenceLoopHandling.Ignore
        };
        var result = JsonConvert.SerializeObject(list, Formatting.Indented, json);
        return new JsonResult { Data = result, MaxJsonLength = int.MaxValue };
Steven Hernández
fuente
0

hay otro caso: los datos se envían del cliente al servidor. cuando está utilizando el método del controlador y el modelo es enorme:

    [HttpPost]
    public ActionResult AddOrUpdateConsumerFile(FileMetaDataModelView inputModel)
    {
        if (inputModel == null) return null;
     ....
    }

el sistema arroja una excepción como esta "Error durante la serialización o deserialización utilizando JSON JavaScriptSerializer. La longitud de la cadena excede el valor establecido en la propiedad maxJsonLength. Nombre del parámetro: entrada"

Solo cambiar la configuración de Web.config no es suficiente para ayudar en este caso. También puede anular el serializador mvc json para admitir grandes tamaños de modelo de datos o deserializar manualmente el modelo desde Solicitud. Su método de controlador se convierte en:

   [HttpPost]
    public ActionResult AddOrUpdateConsumerFile()
    {
        FileMetaDataModelView inputModel = RequestManager.GetModelFromJsonRequest<FileMetaDataModelView>(HttpContext.Request);
        if (inputModel == null) return null;
        ......
    }

   public static T GetModelFromJsonRequest<T>(HttpRequestBase request)
    {
        string result = "";
        using (Stream req = request.InputStream)
        {
            req.Seek(0, System.IO.SeekOrigin.Begin);
            result = new StreamReader(req).ReadToEnd();
        }
        return JsonConvert.DeserializeObject<T>(result);
    }
Oleg Bondarenko
fuente
0

Puede poner este código en cshtml si está devolviendo la vista desde el controlador y desea aumentar la longitud de los datos de la bolsa de vista mientras codifica en json en cshtml

@{
    var jss = new System.Web.Script.Serialization.JavaScriptSerializer();
    jss.MaxJsonLength = Int32.MaxValue;
    var userInfoJson = jss.Serialize(ViewBag.ActionObj);
}

var dataJsonOnActionGrid1 = @Html.Raw(userInfoJson);

Ahora, dataJsonOnActionGrid1 será accesible en la página js y obtendrá el resultado adecuado.

Gracias

Deepak Singhal
fuente