MaxJsonLength exception in ASP.NET MVC pendant JavaScriptSerializer
dans l'une de mes actions de contrôleur, je renvoie un très grand JsonResult
pour remplir une grille.
je suis le suivant InvalidOperationException
exception:
erreur lors de la sérialisation ou de la desérialisation en utilisant le JavaScriptSerializer JSON. La longueur de la chaîne dépasse la valeur définie sur la propriété maxJsonLength.
fixant la propriété maxJsonLength
dans le web.config
à un plus haut la valeur ne montre malheureusement aucun effet.
<system.web.extensions>
<scripting>
<webServices>
<jsonSerialization maxJsonLength="2147483644"/>
</webServices>
</scripting>
</system.web.extensions>
Je ne veux pas le renvoyer comme une chaîne comme mentionné dans ce alors répondez.
dans ma recherche, je suis tombé sur ce post de blog où l'écriture d'un propre ActionResult
(par exemple LargeJsonResult : JsonResult
) est recommandé pour contourner ce comportement.
Est-ce la seule solution?
Est - ce un bug dans ASP.NET MVC?
Ai-je raté quelque chose?
Toute aide serait appréciée.
11 réponses
il semble que cela ait été fixé 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;
}
vous pouvez aussi utiliser ContentResult
comme suggéré ici au lieu de sousclasser JsonResult
.
var serializer = new JavaScriptSerializer { MaxJsonLength = Int32.MaxValue, RecursionLimit = 100 };
return new ContentResult()
{
Content = serializer.Serialize(data),
ContentType = "application/json",
};
malheureusement le web.le paramètre de configuration est ignoré par l'implémentation par défaut JsonResult . Donc je suppose que vous aurez besoin d'implémenter un résultat JSON personnalisé pour surmonter ce problème.
pas besoin de classe personnalisée. C'est tout ce qui est nécessaire:
return new JsonResult { Data = Result, MaxJsonLength = Int32.MaxValue };
où Result
est cette donnée que vous souhaitez sérialiser.
j'ai résolu le problème en suivant cette 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());
}
vous pouvez essayer de définir dans votre expression LINQ seulement le champ dont vous aurez besoin.
exemple. Imaginez que vous ayez un modèle avec Id, Nom, téléphone et image (byte array) et que vous ayez besoin de charger à partir de json dans une liste de sélection.
LINQ Query:
var listItems = (from u in Users where u.name.Contains(term) select u).ToList();
le problème ici est " sélectionnez u " qui obtiennent 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();
les meilleures pratiques sont sélectionnez seulement le domaine que vous allez utiliser.
rappelez-vous. C'est un conseil simple, mais peut aider beaucoup ASP.NET MVC developpers.
Alternative ASP.NET fixation MVC 5:
dans mon cas, l'erreur s'est produite pendant la demande. La meilleure approche dans mon scénario est de modifier le JsonValueProviderFactory
qui applique le correctif au projet global et peut être fait en éditant le fichier global.cs
comme tel.
JsonValueProviderConfig.Config(ValueProviderFactories.Factories);
ajouter un web.entrée de configuration:
<add key="aspnet:MaxJsonLength" value="20971520" />
et ensuite créer 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.Mvc
mais avec l'ajout d'un web configurable.config appsetting valeur 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;
}
}
vous devez lire la section configuration manuellement avant que votre code renvoie un objet JsonResult. Il suffit de lire à partir du web.config en ligne simple:
var jsonResult = Json(resultsForAjaxUI);
jsonResult.MaxJsonLength = (ConfigurationManager.GetSection("system.web.extensions/scripting/webServices/jsonSerialization") as System.Web.Configuration.ScriptingJsonSerializationSection).MaxJsonLength;
return jsonResult;
assurez-vous que vous avez défini l'élément de configuration dans web.config
rien de ce qui précède n'a fonctionné pour moi jusqu'à ce que je change l'Action en [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...");
}
});
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 };