Exceção MaxJsonLength no ASP.NET MVC durante JavaScriptSerializer

122

Em uma das ações do meu controlador, estou retornando um valor muito grande JsonResultpara preencher uma grade.

Estou recebendo a seguinte InvalidOperationExceptionexceção:

Erro durante a serialização ou desserialização usando o JSON JavaScriptSerializer. O comprimento da cadeia excede o valor definido na propriedade maxJsonLength.

Definir a maxJsonLengthpropriedade em web.configum valor mais alto, infelizmente, não mostra nenhum efeito.

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

Não quero devolvê-lo como uma string, como mencionado nesta resposta do SO.

Na minha pesquisa, me deparei com este post do blog onde escrever um próprio ActionResult(por exemplo LargeJsonResult : JsonResult) é recomendado para contornar esse comportamento.

Essa é a única solução?
Isso é um bug no asp.net MVC?
Estou esquecendo de algo?

Qualquer ajuda seria muito apreciada.

Martin Buberl
fonte
2
Suas soluções funciona em MVC 3.
MatteoSp
1
@ Matteo Você tem certeza? Esta é uma pergunta antiga e não me lembro, mas aparentemente eu a marquei como MVC3. Infelizmente eu não posso ver a versão / data em que ficou fixo / fechado: aspnet.codeplex.com/workitem/3436
Martin Buberl
1
Claro, estou trabalhando com o MVC 3 e funciona. E, felizmente, porque no MVC 3 você não tem as propriedades "MaxJsonLength" citadas na resposta aceita.
precisa saber é o seguinte

Respostas:

228

Parece que isso foi corrigido no MVC4.

Você pode fazer isso, o que funcionou bem para mim:

public ActionResult SomeControllerAction()
{
  var jsonResult = Json(veryLargeCollection, JsonRequestBehavior.AllowGet);
  jsonResult.MaxJsonLength = int.MaxValue;
  return jsonResult;
}
Orion Edwards
fonte
Estou definindo uma string json em uma propriedade ViewBag.MyJsonString, mas obtendo o mesmo erro na minha exibição em tempo de execução na seguinte linha javascript: var myJsonObj = @ Html.Raw (Json.Encode (ViewBag.MyJsonString));
Faisal Mq
1
Hey @orionedvards, @ GG, @ MartinBuberl Estou enfrentando o mesmo problema do maxJson, mas ao postar dados no controlador, como posso lidar com isso, passei tanto tempo pesquisando sobre isso. Qualquer ajuda seria grata.
precisa saber é
No meu caso, não funcionou porque eu tive que definir MaxJsonLength antes de json serializar a coleção.
César León
No meu caso, funciona bem, tive que implementá-lo por causa de "IMAGES" na tabela de dados para apresentar ao usuário final. Sem ele, basta travar sem nenhuma mensagem compreensível.
Mauro Candido
33

Você também pode usar ContentResultcomo sugerido aqui, em vez de subclassificar JsonResult.

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

return new ContentResult()
{
    Content = serializer.Serialize(data),
    ContentType = "application/json",
};
SliverNinja - MSFT
fonte
2
no meu caso, trabalhando em um aplicativo descartável, essa solução funcionou melhor para mim. salvo implementando jsonresult. obrigado!
Christo
22

Não há necessidade de uma classe personalizada. Isso é tudo o que é necessário:

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

onde Resultestão os dados que você deseja serializar.

John
fonte
Erro 137 'System.Web.Mvc.JsonResult' não contém uma definição para 'MaxJsonLength'
PUG
Isso funcionou para mim, no entanto, ainda necessário para adicionar: JsonRequestBehavior = JsonRequestBehavior.AllowGet
DubMan
5

Se você usar o Json.NET para gerar a jsonstring, ele não precisará definir MaxJsonLengthvalor.

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

Resolvi o problema seguindo este link

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

Estou surpreso que ninguém tenha sugerido o uso de um filtro de resultados. Esta é a maneira mais limpa de se conectar globalmente ao pipeline de ação / resultado:

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

Em seguida, registre uma instância dessa classe usando GlobalFilters.Filters:

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

Você pode tentar definir na sua expressão LINQ apenas os campos que você precisará.

Exemplo. Imagine que você tem um modelo com ID, nome, telefone e imagem (matriz de bytes) e precisa carregar do json em uma lista de seleção.

Consulta LINQ:

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

O problema aqui é " selecione u " que obtém todos os campos. Então, se você tem fotos grandes, booomm.

Como resolver? muito, muito simples

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

As práticas recomendadas são selecionar apenas o campo que você usará.

Lembrar. Esta é uma dica simples, mas pode ajudar muitos desenvolvedores do ASP.NET MVC.

glanes
fonte
1
Eu não assumiria que o usuário neste caso deseja filtrar seus dados. Algumas pessoas têm requisitos para trazer de volta uma grande quantidade de linhas do banco de dados ...
Simon Nicholls
2

Correção alternativa do ASP.NET MVC 5:

No meu caso, o erro estava ocorrendo durante a solicitação. A melhor abordagem no meu cenário é modificar o real JsonValueProviderFactoryque aplica a correção ao projeto global e pode ser feito editando o global.csarquivo como tal.

JsonValueProviderConfig.Config(ValueProviderFactories.Factories);

adicione uma entrada web.config:

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

e crie as duas classes a seguir

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

Esta é basicamente uma cópia exata da implementação padrão encontrada em, System.Web.Mvcmas com a adição de um valor configurável de websconfig appsetting 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
fonte
Muito obrigado !
larizzatg
1

Nenhuma das opções acima funcionou para mim até eu alterar a ação como [HttpPost]. e fez o tipo ajax como 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);
            ..
            ..
            ...

    }

E o ajax chama como

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

Foi a correção para mim no MVC 4.

eaglei22
fonte
0

Você precisa ler a seção de configuração manualmente antes de seu código retornar um objeto JsonResult. Basta ler o web.config em uma única linha:

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

Certifique-se de definir o elemento de configuração em web.config

Pavel Nazarov
fonte
0

isso funcionou para mim

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

existe outro caso - os dados são enviados do cliente para o servidor. quando você está usando o método do controlador e o modelo é enorme:

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

O sistema lança uma exceção como esta "Erro durante a serialização ou desserialização usando o JSON JavaScriptSerializer. O comprimento da cadeia excede o valor definido na propriedade maxJsonLength. Nome do parâmetro: input"

Apenas alterar as configurações do Web.config não é suficiente para ajudar nesse caso. Além disso, você pode substituir o mvc json serializer para oferecer suporte a grandes tamanhos de modelo de dados ou desserializar manualmente o modelo de Request. Seu método de controle se torna:

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

Você pode colocar esse código em cshtml se estiver retornando a visualização do controlador e desejar aumentar o comprimento dos dados da bolsa de visualização enquanto codifica em json em cshtml

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

var dataJsonOnActionGrid1 = @Html.Raw(userInfoJson);

Agora, dataJsonOnActionGrid1 será acessível na página js e você obterá o resultado adequado.

obrigado

Deepak Singhal
fonte