Pengecualian MaxJsonLength di ASP.NET MVC selama JavaScriptSerializer

122

Dalam salah satu tindakan pengontrol saya, saya mengembalikan yang sangat besar JsonResultuntuk mengisi kisi.

Saya mendapatkan InvalidOperationExceptionpengecualian berikut :

Kesalahan selama serialisasi atau deserialisasi menggunakan JSON JavaScriptSerializer. Panjang string melebihi nilai yang ditetapkan pada properti maxJsonLength.

Sayangnya, menyetel maxJsonLengthproperti di web.configke nilai yang lebih tinggi tidak menunjukkan efek apa pun.

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

Saya tidak ingin mengirimkannya kembali sebagai string seperti yang disebutkan dalam jawaban SO ini .

Dalam penelitian saya, saya menemukan posting blog ini di mana menulis sendiri ActionResult(misalnya LargeJsonResult : JsonResult) disarankan untuk melewati perilaku ini.

Apakah ini satu-satunya solusi?
Apakah ini bug di ASP.NET MVC?
Apakah saya melewatkan sesuatu?

Bantuan apa pun akan sangat dihargai.

Martin Buberl
sumber
2
Solusi Anda berfungsi pada MVC 3.
MatteoSp
1
@Matteo Apakah Anda yakin? Ini adalah pertanyaan lama dan saya tidak ingat tetapi ternyata saya menandainya sebagai MVC3. Sayangnya saya tidak dapat melihat versi / tanggal ketika diperbaiki / ditutup: aspnet.codeplex.com/workitem/3436
Martin Buberl
1
Tentu, saya bekerja dengan MVC 3 dan berhasil. Dan untungnya, karena di MVC 3 Anda tidak memiliki properti "MaxJsonLength" yang dikutip dalam jawaban yang diterima.
MatteoSp

Jawaban:

228

Tampaknya ini telah diperbaiki di MVC4.

Anda bisa melakukan ini, yang berhasil untuk saya:

public ActionResult SomeControllerAction()
{
  var jsonResult = Json(veryLargeCollection, JsonRequestBehavior.AllowGet);
  jsonResult.MaxJsonLength = int.MaxValue;
  return jsonResult;
}
Orion Edwards
sumber
Saya menyetel string json menjadi properti ViewBag.MyJsonString tetapi mendapatkan kesalahan yang sama dalam tampilan saya saat runtime pada baris javascript berikut: var myJsonObj = @ Html.Raw (Json.Encode (ViewBag.MyJsonString));
Faisal Mq
1
Hai @orionedvards, @ GG, @ MartinBuberl Saya menghadapi masalah maxJson yang sama tetapi ketika memposting data ke pengontrol, Bagaimana saya dapat menangani ini, saya menghabiskan banyak waktu untuk mencari tentang ini. Bantuan apa pun akan berterima kasih.
katmanco
Dalam kasus saya tidak berhasil karena saya harus mengatur MaxJsonLength sebelum json membuat serial koleksi.
César León
Dalam kasus saya berfungsi dengan baik, saya harus menerapkannya karena "GAMBAR" di datatable untuk disajikan kepada pengguna akhir. Tanpa itu, crash begitu saja tanpa pesan yang bisa dimengerti.
Mauro Candido
33

Anda juga dapat menggunakan ContentResultseperti yang disarankan di sini daripada membuat subclass JsonResult.

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

return new ContentResult()
{
    Content = serializer.Serialize(data),
    ContentType = "application/json",
};
SliverNinja - MSFT
sumber
2
dalam kasus saya, mengerjakan aplikasi sekali pakai, solusi ini bekerja paling baik untuk saya. disimpan menerapkan jsonresult. Terima kasih!
Christo
22

Tidak perlu kelas khusus. Ini semua yang dibutuhkan:

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

di mana Resultdata yang ingin Anda buat serialnya.

John
sumber
Galat 137 'System.Web.Mvc.JsonResult' tidak berisi definisi untuk 'MaxJsonLength'
PUG
Ini berhasil untuk saya, namun masih perlu menambahkan: JsonRequestBehavior = JsonRequestBehavior.AllowGet
DubMan
5

Jika menggunakan Json.NET untuk menghasilkan jsonstring, itu tidak perlu menyetel MaxJsonLengthnilai.

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

Saya memecahkan masalah dengan mengikuti tautan ini

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

Saya terkejut tidak ada yang menyarankan menggunakan filter hasil. Ini adalah cara terbersih untuk menghubungkan secara global ke dalam pipa tindakan / hasil:

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

Kemudian, daftarkan sebuah instance dari kelas itu menggunakan GlobalFilters.Filters:

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

Anda dapat mencoba mendefinisikan dalam ekspresi LINQ Anda hanya bidang yang Anda perlukan.

Contoh. Bayangkan Anda memiliki Model dengan Id, Name, Phone dan Picture (byte array) dan perlu memuat dari json ke dalam daftar pilih.

Kueri LINQ:

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

Masalahnya di sini adalah " pilih u " yang mendapatkan semua bidang. Jadi, jika Anda memiliki gambaran besar, booomm.

Bagaimana menyelesaikan? sangat, sangat sederhana.

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

Praktik terbaik adalah memilih hanya bidang yang akan Anda gunakan.

Ingat. Ini adalah tip sederhana, tetapi dapat membantu banyak pengembang ASP.NET MVC.

glanes
sumber
1
Saya tidak akan berasumsi bahwa pengguna dalam kasus ini ingin memfilter data mereka. Beberapa orang memiliki persyaratan untuk mengembalikan sejumlah besar baris dari database ...
Simon Nicholls
2

Perbaikan ASP.NET MVC 5 alternatif:

Dalam kasus saya, kesalahan terjadi selama permintaan. Pendekatan terbaik dalam skenario saya adalah memodifikasi aktual JsonValueProviderFactoryyang menerapkan perbaikan pada proyek global dan dapat dilakukan dengan mengedit global.csfile seperti itu.

JsonValueProviderConfig.Config(ValueProviderFactories.Factories);

tambahkan entri web.config:

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

lalu buat dua kelas berikut

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

Ini pada dasarnya adalah salinan persis dari implementasi default yang ditemukan System.Web.Mvctetapi dengan tambahan nilai appsetting web.config yang dapat dikonfigurasi 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
sumber
Terima kasih banyak !
larizzatg
1

Tak satu pun di atas berhasil bagi saya sampai saya mengubah Tindakan sebagai [HttpPost]. dan menjadikan tipe ajax sebagai 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);
            ..
            ..
            ...

    }

Dan panggilan ajax sebagai

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

Apakah perbaikan untuk saya di MVC 4.

eaglei22
sumber
0

Anda perlu membaca dari bagian konfigurasi secara manual sebelum kode Anda mengembalikan objek JsonResult. Cukup baca dari web.config dalam satu baris:

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

Pastikan Anda mendefinisikan elemen konfigurasi di web.config

Pavel Nazarov
sumber
0

ini berhasil untuk saya

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

ada kasus lain - data dikirim dari klien ke server. ketika Anda menggunakan metode dan model pengontrol sangat besar:

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

sistem memunculkan pengecualian seperti ini "Kesalahan selama serialisasi atau deserialisasi menggunakan JSON JavaScriptSerializer. Panjang string melebihi nilai yang ditetapkan pada properti maxJsonLength. Nama parameter: input"

Hanya mengubah pengaturan Web.config tidak cukup untuk membantu dalam kasus ini. Anda juga dapat mengganti serializer mvc json untuk mendukung ukuran model data yang besar atau secara manual menghapus model dari Request. Metode pengontrol Anda menjadi:

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

Anda dapat meletakkan kode ini di cshtml jika Anda mengembalikan tampilan dari pengontrol dan Anda ingin menambah panjang data tampilan bag saat melakukan encoding di json di cshtml

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

var dataJsonOnActionGrid1 = @Html.Raw(userInfoJson);

Sekarang, dataJsonOnActionGrid1 akan dapat diakses di halaman js dan Anda akan mendapatkan hasil yang tepat.

Terima kasih

Deepak Singhal
sumber