MaxJsonLength-Ausnahme in ASP.NET MVC während JavaScriptSerializer

122

Bei einer meiner Controller-Aktionen gebe ich ein sehr großes zurück JsonResult, um ein Raster zu füllen.

Ich erhalte die folgende InvalidOperationExceptionAusnahme:

Fehler während der Serialisierung oder Deserialisierung mit dem JSON JavaScriptSerializer. Die Länge der Zeichenfolge überschreitet den in der Eigenschaft maxJsonLength festgelegten Wert.

Das Setzen der maxJsonLengthEigenschaft web.configauf einen höheren Wert zeigt leider keine Auswirkung.

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

Ich möchte es nicht als Zeichenfolge zurückgeben, wie in dieser SO-Antwort erwähnt.

Bei meinen Recherchen bin ich auf diesen Blog-Beitrag gestoßen, in dem das Schreiben eines eigenen ActionResult(z. B. LargeJsonResult : JsonResult) empfohlen wird, um dieses Verhalten zu umgehen.

Ist das dann die einzige Lösung?
Ist dies ein Fehler in ASP.NET MVC?
Vermisse ich etwas

Jede Hilfe wäre sehr dankbar.

Martin Buberl
quelle
2
Ihre Lösungen funktionieren auf MVC 3.
MatteoSp
1
@ Matto Bist du sicher? Dies ist eine alte Frage, an die ich mich nicht erinnern kann, aber anscheinend habe ich sie als MVC3 markiert. Leider kann ich die Version / das Datum nicht sehen, an dem es repariert
Martin Buberl
1
Klar, ich arbeite mit MVC 3 und es funktioniert. Und zum Glück, weil Sie in MVC 3 nicht die Eigenschaften "MaxJsonLength" haben, die in der akzeptierten Antwort angegeben sind.
MatteoSp

Antworten:

228

Es scheint, dass dies in MVC4 behoben wurde.

Sie können dies tun, was für mich gut funktioniert hat:

public ActionResult SomeControllerAction()
{
  var jsonResult = Json(veryLargeCollection, JsonRequestBehavior.AllowGet);
  jsonResult.MaxJsonLength = int.MaxValue;
  return jsonResult;
}
Orion Edwards
quelle
Ich setze eine JSON-Zeichenfolge in eine ViewBag.MyJsonString-Eigenschaft, erhalte jedoch zur Laufzeit denselben Fehler in meiner Ansicht in der folgenden Javascript-Zeile: var myJsonObj = @ Html.Raw (Json.Encode (ViewBag.MyJsonString));
Faisal Mq
1
Hey @oruledvards, @ GG, @ MartinBuberl Ich habe das gleiche maxJson-Problem, aber als ich Daten an den Controller postete, wie kann ich damit umgehen, habe ich so viel Zeit damit verbracht, darüber zu suchen. Jede Hilfe wäre dankbar.
Katmanco
In meinem Fall hat es nicht funktioniert, weil ich MaxJsonLength einstellen musste, bevor json die Sammlung serialisiert.
César León
In meinem Fall funktioniert gut, ich musste es wegen "IMAGES" in datatable implementieren, um es für den Endbenutzer zu präsentieren. Ohne es einfach ohne verständliche Nachricht abstürzen.
Mauro Candido
33

Sie können auch ContentResultwie hier vorgeschlagen anstelle von Unterklassen verwenden JsonResult.

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

return new ContentResult()
{
    Content = serializer.Serialize(data),
    ContentType = "application/json",
};
SliverNinja - MSFT
quelle
2
In meinem Fall, als ich an einer Wegwerf-App arbeitete, funktionierte diese Lösung am besten für mich. gespeicherte Implementierung von jsonresult gespeichert. Vielen Dank!
Christo
22

Keine benutzerdefinierte Klasse erforderlich. Das ist alles was benötigt wird:

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

Wo Resultsind die Daten, die Sie serialisieren möchten?

John
quelle
Fehler 137 'System.Web.Mvc.JsonResult' enthält keine Definition für 'MaxJsonLength'
PUG
Dies funktionierte für mich, musste jedoch noch hinzugefügt werden: JsonRequestBehavior = JsonRequestBehavior.AllowGet
DubMan
5

Wenn Sie Json.NET zum Generieren der jsonZeichenfolge verwenden, muss kein MaxJsonLengthWert festgelegt werden.

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

Ich habe das Problem gelöst, indem ich diesem Link gefolgt bin

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
quelle
3

Ich bin überrascht, dass niemand vorgeschlagen hat, einen Ergebnisfilter zu verwenden. Dies ist der sauberste Weg, um sich global in die Aktions- / Ergebnis-Pipeline einzubinden:

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

Registrieren Sie dann eine Instanz dieser Klasse mit GlobalFilters.Filters:

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

Sie können versuchen, in Ihrem LINQ-Ausdruck nur die Felder zu definieren, die Sie benötigen.

Beispiel. Stellen Sie sich vor, Sie haben ein Modell mit ID, Name, Telefon und Bild (Byte-Array) und müssen von json in eine Auswahlliste laden.

LINQ-Abfrage:

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

Das Problem hier ist " select u ", das alle Felder erhält. Also, wenn Sie große Bilder haben, booomm.

Wie löst man? sehr, sehr einfach.

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

Am besten wählen Sie nur das Feld aus, das Sie verwenden möchten.

Merken. Dies ist ein einfacher Tipp, kann aber vielen ASP.NET MVC-Entwicklern helfen.

glanes
quelle
1
Ich würde nicht annehmen, dass der Benutzer in diesem Fall seine Daten filtern möchte. Einige Leute haben Anforderungen, um eine große Anzahl von Zeilen aus der Datenbank zurückzubringen ...
Simon Nicholls
2

Alternative ASP.NET MVC 5 Fix:

In meinem Fall ist der Fehler während der Anfrage aufgetreten. Der beste Ansatz in meinem Szenario ist das Ändern des tatsächlichen Ansatzes, JsonValueProviderFactoryder den Fix auf das globale Projekt anwendet und durch Bearbeiten der global.csDatei als solche erfolgen kann.

JsonValueProviderConfig.Config(ValueProviderFactories.Factories);

Fügen Sie einen web.config-Eintrag hinzu:

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

und erstellen Sie dann die beiden folgenden Klassen

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

Dies ist im Grunde eine exakte Kopie der Standardimplementierung in, System.Web.Mvcjedoch mit dem Zusatz eines konfigurierbaren Web.config-Appsetting-Werts 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
quelle
Ich danke dir sehr !
Larizzatg
1

Nichts davon hat für mich geklappt, bis ich die Aktion als geändert habe [HttpPost]. und machte den Ajax-Typ als 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);
            ..
            ..
            ...

    }

Und der Ajax ruft als

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

War das Update für mich in MVC 4.

eaglei22
quelle
0

Sie müssen manuell aus dem Konfigurationsabschnitt lesen, bevor Ihr Code ein JsonResult-Objekt zurückgibt. Lesen Sie einfach in einer Zeile aus web.config:

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

Stellen Sie sicher, dass Sie das Konfigurationselement in web.config definiert haben

Pavel Nazarov
quelle
0

das hat bei mir funktioniert

        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
quelle
0

Es gibt einen anderen Fall: Daten werden vom Client an den Server gesendet. Wenn Sie die Controller-Methode und das Controller-Modell verwenden, ist dies enorm:

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

Das System löst eine Ausnahme wie diese aus: "Fehler beim Serialisieren oder Deserialisieren mit dem JSON JavaScriptSerializer. Die Länge der Zeichenfolge überschreitet den in der Eigenschaft maxJsonLength festgelegten Wert. Parametername: Eingabe"

Nur das Ändern der Web.config-Einstellungen reicht in diesem Fall nicht aus. Sie können den mvc json-Serializer zusätzlich überschreiben, um große Datenmodellgrößen zu unterstützen, oder das Modell manuell von Request deserialisieren. Ihre Controller-Methode wird:

   [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
quelle
0

Sie können diesen Code in cshtml einfügen, wenn Sie die Ansicht vom Controller zurückgeben und die Länge der View-Bag-Daten erhöhen möchten, während Sie in json in cshtml codieren

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

var dataJsonOnActionGrid1 = @Html.Raw(userInfoJson);

Jetzt dataJsonOnActionGrid1 wird auf js Seite zugänglich sein und Sie erhalten das richtige Ergebnis.

Vielen Dank

Deepak Singhal
quelle