Dans l'une de mes actions de contrôleur, je renvoie une très grande JsonResult
pour remplir une grille.
Je reçois l'exception InvalidOperationException
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 dans la propriété maxJsonLength.
Définir la propriété maxJsonLength
dans le web.config
sur une valeur supérieure n’a 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 indiqué dans this SO répondre.
Dans mes recherches, je suis tombé sur this blog où il est recommandé d’écrire une variable ActionResult
(exemple: LargeJsonResult : JsonResult
) pour éviter ce comportement.
Est-ce alors la seule solution?
Est-ce un bogue dans ASP.NET MVC?
Est-ce que je manque quelque chose?
Toute aide serait appréciée.
Il semble que cela ait été corrigé dans MVC4.
Vous pouvez le faire, ce qui a bien fonctionné pour moi:
public ActionResult SomeControllerAction()
{
var jsonResult = Json(veryLargeCollection, JsonRequestBehavior.AllowGet);
jsonResult.MaxJsonLength = int.MaxValue;
return jsonResult;
}
Vous pouvez également utiliser ContentResult
comme 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",
};
Malheureusement, le paramètre web.config est ignoré par l'implémentation JsonResult par défaut . Donc, je suppose que vous devrez implémenter un résultat JSON personnalisé pour surmonter ce problème.
Pas besoin d'un cours personnalisé. C'est tout ce qui est nécessaire:
return new JsonResult { Data = Result, MaxJsonLength = Int32.MaxValue };
où Result
correspond aux données que vous souhaitez sérialiser.
Si vous utilisez Json.NET pour générer la chaîne json
, il n'est pas nécessaire de définir la valeur MaxJsonLength
.
return new ContentResult()
{
Content = Newtonsoft.Json.JsonConvert.SerializeObject(data),
ContentType = "application/json",
};
J'ai résolu le problème en suivant cecilink
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 uniquement les champs dont vous aurez besoin.
Exemple. Imaginez que vous avez un modèle avec Id, Nom, Téléphone et Image (tableau d'octets) et que vous devez charger 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 " select u " qui récupère tous les champs. Donc, si vous avez de grandes images, 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 consistent à sélectionner uniquement le champ que vous utiliserez.
Rappelles toi. Ceci est un conseil simple, mais peut aider de nombreux développeurs ASP.NET MVC.
Solution alternative à 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 la JsonValueProviderFactory
réelle qui applique le correctif au projet global et peut être effectuée en modifiant le fichier global.cs
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.Mvc
, mais avec l’ajout d’une valeur configurable d’application 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;
}
}
Vous devez lire la section de configuration manuellement avant que votre code ne retourne un objet JsonResult. Il suffit de lire 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 que vous avez défini l'élément de configuration dans web.config
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 };
Vous pouvez mettre ce code dans cshtml si vous retournez une vue depuis le contrôleur et que vous souhaitez augmenter la longueur des données du panier de vues 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
il y a un peu autre cas - les données sont envoyées du client au serveur. lorsque vous utilisez la méthode du contrôleur et que 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 pour la propriété maxJsonLength. Nom du paramètre: entrée"
Changer les paramètres Web.config ne suffit pas pour vous aider dans ce cas. Vous pouvez en outre substituer 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 à partir de la demande. 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);
}
Rien de ce qui précède n'a fonctionné pour moi jusqu'à ce que je modifie l'action en [HttpPost]
. et fait que le type ajax est comme suit 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...");
}
});
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
};
}
La solution pour moi dans MVC 4.