Exception MaxJsonLength dans ASP.NET MVC pendant JavaScriptSerializer


122

Dans l'une de mes actions de contrôleur, je retourne un très grand JsonResultpour remplir une grille.

Je reçois l' InvalidOperationExceptionexception suivante :

Erreur lors de la sérialisation ou de la désérialisation à l'aide de JSON JavaScriptSerializer. La longueur de la chaîne dépasse la valeur définie sur la propriété maxJsonLength.

La définition de la maxJsonLengthpropriété dans web.configune valeur plus élevée ne montre malheureusement aucun effet.

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

Je ne veux pas le renvoyer sous forme de chaîne comme mentionné dans cette réponse SO.

Dans mes recherches, je suis tombé sur ce billet de blog où il est recommandé d'en écrire un ActionResult(par exemple LargeJsonResult : JsonResult) pour contourner ce comportement.

Est-ce donc la seule solution?
S'agit-il d'un bogue dans ASP.NET MVC?
Est-ce que je manque quelque chose?

Toute aide sera très appréciée.


2
Vos solutions fonctionnent sur MVC 3.
MatteoSp

1
@Matteo Êtes-vous sûr? C'est une vieille question et je ne me souviens plus mais apparemment je l'ai étiquetée comme MVC3. Malheureusement, je ne peux pas voir la version / la date à laquelle il a été corrigé / fermé: aspnet.codeplex.com/workitem/3436
Martin Buberl

1
Bien sûr, je travaille avec MVC 3 et cela fonctionne. Et heureusement, car dans MVC 3 vous n'avez pas les propriétés "MaxJsonLength" citées dans la réponse acceptée.
MatteoSp

Réponses:


228

Il semble que cela ait été corrigé dans MVC4.

Vous pouvez faire ceci, qui a bien fonctionné pour moi:

public ActionResult SomeControllerAction()
{
  var jsonResult = Json(veryLargeCollection, JsonRequestBehavior.AllowGet);
  jsonResult.MaxJsonLength = int.MaxValue;
  return jsonResult;
}

Je définis une chaîne json dans une propriété ViewBag.MyJsonString mais j'obtiens la même erreur dans ma vue au moment de l'exécution sur la ligne javascript suivante: var myJsonObj = @ Html.Raw (Json.Encode (ViewBag.MyJsonString));
Faisal Mq

1
Hey @oreledvards, @ GG, @ MartinBuberl Je suis confronté au même problème maxJson, mais lorsque je publie des données sur le contrôleur, comment puis-je gérer cela, j'ai passé tellement de temps à chercher à ce sujet. Toute aide serait reconnaissante.
katmanco

Dans mon cas, cela n'a pas fonctionné car j'ai dû définir MaxJsonLength avant que json ne sérialise la collection.
César León

Dans mon cas fonctionne très bien, j'ai dû l'implémenter à cause des "IMAGES" dans datatable à présenter pour l'utilisateur final. Sans cela, plantez simplement sans aucun message compréhensible.
Mauro Candido le

33

Vous pouvez également utiliser ContentResultcomme suggéré ici au lieu de sous-classer JsonResult.

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

return new ContentResult()
{
    Content = serializer.Serialize(data),
    ContentType = "application/json",
};

2
dans mon cas, en travaillant sur une application jetable, cette solution fonctionnait mieux pour moi. enregistré en implémentant jsonresult. Merci!
Christo


22

Pas besoin de classe personnalisée. C'est tout ce qu'il faut:

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

Resultsont ces données que vous souhaitez sérialiser.


Erreur 137 'System.Web.Mvc.JsonResult' ne contient pas de définition pour 'MaxJsonLength'
PUG

Cela a fonctionné pour moi, mais il fallait encore ajouter: JsonRequestBehavior = JsonRequestBehavior.AllowGet
DubMan

5

Si vous utilisez Json.NET pour générer la jsonchaîne, il n'est pas nécessaire de définir la MaxJsonLengthvaleur.

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

4

J'ai résolu le problème en suivant ce lien

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());
    }

3

Je suis surpris que personne n'ait suggéré d'utiliser un filtre de résultat. C'est le moyen le plus propre de se connecter globalement au pipeline action / résultat:

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)
    {
    }
}

Ensuite, enregistrez une instance de cette classe en utilisant GlobalFilters.Filters:

GlobalFilters.Filters.Add(new JsonResultFilter { MaxJsonLength = int.MaxValue });

2

Vous pouvez essayer de définir dans votre expression LINQ uniquement les champs dont vous aurez besoin.

Exemple. Imaginez que vous ayez un modèle avec un identifiant, un nom, un téléphone et une image (tableau d'octets) et que vous deviez charger depuis json dans une liste de sélection.

Requête LINQ:

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

Le problème ici est " sélectionnez u " qui obtient tous les champs. Donc, si vous avez de grandes photos, booomm.

Comment résoudre? très, très simple.

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

La meilleure pratique consiste à sélectionner uniquement le champ que vous utiliserez.

Rappelles toi. C'est une astuce simple, mais qui peut aider de nombreux développeurs ASP.NET MVC.


1
Je ne suppose pas que l'utilisateur dans ce cas souhaite filtrer ses données. Certaines personnes ont des exigences pour ramener une grande quantité de lignes de la base de données ...
Simon Nicholls

2

Correction alternative d'ASP.NET MVC 5:

Dans mon cas, l'erreur s'est produite lors de la demande. La meilleure approche dans mon scénario consiste à modifier le réel JsonValueProviderFactoryqui applique le correctif au projet global et peut être fait en éditant le global.csfichier en tant que tel.

JsonValueProviderConfig.Config(ValueProviderFactories.Factories);

ajoutez une entrée web.config:

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

puis créez les deux classes suivantes

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

Il s'agit essentiellement d'une copie exacte de l'implémentation par défaut trouvée dans System.Web.Mvcmais avec l'ajout d'une valeur de paramétrage d'application web.config configurable 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;
    }
}

Merci beaucoup !
larizzatg

1

Rien de ce qui précède n'a fonctionné pour moi jusqu'à ce que je change l'action en tant que [HttpPost]. et a fait le type ajax comme 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);
            ..
            ..
            ...

    }

Et l'appel ajax comme

$.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...");
    }
});

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
        };
    }

Était le correctif pour moi dans MVC 4.


0

Vous devez lire manuellement la section de configuration avant que votre code ne renvoie un objet JsonResult. Lisez simplement à partir de web.config en une seule ligne:

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

Assurez-vous d'avoir défini l'élément de configuration dans web.config


0

cela a fonctionné pour moi

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

0

il y a un autre cas - les données sont envoyées du client au serveur. lorsque vous utilisez la méthode du contrôleur et le modèle est énorme:

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

Le système lève une exception comme celle-ci "Erreur lors de la sérialisation ou de la désérialisation à l'aide de JSON JavaScriptSerializer. La longueur de la chaîne dépasse la valeur définie sur la propriété maxJsonLength. Nom du paramètre: entrée"

Seule la modification des paramètres Web.config n'est pas suffisante pour aider dans ce cas. Vous pouvez en outre remplacer le sérialiseur mvc json pour prendre en charge des tailles de modèle de données énormes ou désérialiser manuellement le modèle de Request. Votre méthode de contrôleur devient:

   [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);
    }

0

Vous pouvez mettre ce code dans cshtml si vous renvoyez la vue du contrôleur et que vous souhaitez augmenter la longueur des données du sac de vue lors de l'encodage en json dans cshtml

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

var dataJsonOnActionGrid1 = @Html.Raw(userInfoJson);

Maintenant, dataJsonOnActionGrid1 sera accessible sur la page js et vous obtiendrez un résultat correct.

Merci

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.