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.

Martin Buberl
la source
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;
}
Orion Edwards
la source
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",
};
SliverNinja - MSFT
la source
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.

John
la source
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",
};
AechoLiu
la source
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());
    }
Sajjad Ali Khan
la source
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 });
Ronnie Overby
la source
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.

glanes
la source
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;
    }
}
Maxim Gershkovich
la source
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...");
    }
});
jAntoni
la source
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.

eaglei22
la source
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

Pavel Nazarov
la source
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 };
Steven Hernández
la source
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);
    }
Oleg Bondarenko
la source
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

Deepak Singhal
la source