使用修改后的JsonValueProviderFactory解决maxJsonLength异常后原始数据发生变化

问题描述 投票:0回答:2

我正在使用一些使用 ASP.NET MVC 设计的应用程序。 是否花了很多时间试图解决一些问题,但不知道该怎么做才能解决它。

如下所示的大 JSON 的类似代码将抛出异常: “使用 JSON JavaScriptSerializer 进行序列化或反序列化期间出错。字符串的长度超出了 maxJsonLength 属性上设置的值。 ” 示例:

$http.post('/API/PostData',aoData)...

其中 aoData 等于 3K JSON 数组等

添加了在 stackoverflow 上提出的许多问题中建议的一些解决方案。

解决了这个问题:

  1. 从 ValueProviderFactories.Factories 中删除 JsonValueProviderFactory
  2. 并通过简单修改添加原始类的副本,例如:

示例:

public sealed class LargeJsonValueProviderFactory : ValueProviderFactory
{
private static void AddToBackingStore(LargeJsonValueProviderFactory.EntryLimitedDictionary backingStore, string prefix, object value)
{
    IDictionary<string, object> dictionary = value as IDictionary<string, object>;
    if (dictionary != null)
    {
        foreach (KeyValuePair<string, object> keyValuePair in (IEnumerable<KeyValuePair<string, object>>) dictionary)
            LargeJsonValueProviderFactory.AddToBackingStore(backingStore, LargeJsonValueProviderFactory.MakePropertyKey(prefix, keyValuePair.Key), keyValuePair.Value);
    }
    else
    {
        IList list = value as IList;
        if (list != null)
        {
            for (int index = 0; index < list.Count; ++index)
                LargeJsonValueProviderFactory.AddToBackingStore(backingStore, LargeJsonValueProviderFactory.MakeArrayKey(prefix, index), list[index]);
        }
        else
            backingStore.Add(prefix, value);
    }
}

private static object GetDeserializedObject(ControllerContext controllerContext)
{
    if (!controllerContext.HttpContext.Request.ContentType.StartsWith("application/json", StringComparison.OrdinalIgnoreCase))
        return (object) null;
    string end = new StreamReader(controllerContext.HttpContext.Request.InputStream).ReadToEnd();
    if (string.IsNullOrEmpty(end))
        return (object) null;

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

    return serializer.DeserializeObject(end);
}

/// <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 = LargeJsonValueProviderFactory.GetDeserializedObject(controllerContext);
    if (deserializedObject == null)
        return (IValueProvider) null;
    Dictionary<string, object> dictionary = new Dictionary<string, object>((IEqualityComparer<string>) StringComparer.OrdinalIgnoreCase);
    LargeJsonValueProviderFactory.AddToBackingStore(new LargeJsonValueProviderFactory.EntryLimitedDictionary((IDictionary<string, object>) dictionary), string.Empty, deserializedObject);
    return (IValueProvider) new DictionaryValueProvider<object>((IDictionary<string, object>) dictionary, CultureInfo.CurrentCulture);
}

private static string MakeArrayKey(string prefix, int index)
{
    return prefix + "[" + index.ToString((IFormatProvider) CultureInfo.InvariantCulture) + "]";
}

private static string MakePropertyKey(string prefix, string propertyName)
{
    if (!string.IsNullOrEmpty(prefix))
        return prefix + "." + propertyName;
    return propertyName;
}

private class EntryLimitedDictionary
{
    private static int _maximumDepth = LargeJsonValueProviderFactory.EntryLimitedDictionary.GetMaximumDepth();
    private readonly IDictionary<string, object> _innerDictionary;
    private int _itemCount;

    public EntryLimitedDictionary(IDictionary<string, object> innerDictionary)
    {
        this._innerDictionary = innerDictionary;
    }

    public void Add(string key, object value)
    {
        if (++this._itemCount > LargeJsonValueProviderFactory.EntryLimitedDictionary._maximumDepth)
            throw new InvalidOperationException("JsonValueProviderFactory_RequestTooLarge");
        this._innerDictionary.Add(key, value);
    }

    private static int GetMaximumDepth()
    {
        NameValueCollection appSettings = ConfigurationManager.AppSettings;
        if (appSettings != null)
        {
            string[] values = appSettings.GetValues("aspnet:MaxJsonDeserializerMembers");
            int result;
            if (values != null && values.Length > 0 && int.TryParse(values[0], out result))
                return result;
        }
        return 1000;
     }
  }
}

这解决了 maxJsonLength 的问题。伟大的!但是...

如果 JSON 包含名为 ACTION 的属性,控制器将获取正在更改的数据。 ACTION 属性包含控制器操作的名称而不是“MAR”。 LargeJsonValueProviderFactory 类不会更改 ACION 属性的值。但如果上面显示的 LargeJsonValueProviderFactory 类不是,则使用问题就会消失。

示例:

{ NR:1200, 行动:“三月”, …… }

public ActionResult Save(PrsentationEntity aoData)
{
  aoData.NR equals 1200 - OK
  aoData.ACTION equals "Save" -Should be "MAR"

你知道我为什么会遇到这个问题吗?

问候 马尔辛

asp.net-mvc
2个回答
1
投票

tl;博士

配置应用程序时,原始

JsonValueProviderFactory
应由自定义 LargeJsonValueProviderFactory
替换
,而不是添加到集合的末尾。


长版

您说您通过以下方式解决了问题:

  1. JsonValueProviderFactory
     中删除 
    ValueProviderFactories.Factories
  2. 并通过简单修改添加原始类的副本,例如:

这就是问题出现的原因。

ValueProviderFactories.Factories
上工厂的顺序确实很重要,但一般不讨论。

原来的订单是这个:

private static readonly ValueProviderFactoryCollection _factories = new ValueProviderFactoryCollection()
        {
            new ChildActionValueProviderFactory(),
            new FormValueProviderFactory(),
            new JsonValueProviderFactory(),
            new RouteDataValueProviderFactory(),
            new QueryStringValueProviderFactory(),
            new HttpFileCollectionValueProviderFactory(),
            new JQueryFormValueProviderFactory()
        };

如果您只是将新的提供程序添加到集合的末尾,那么如果其他提供程序之一完成了这项工作,则不会使用该提供程序(在这种情况下,似乎使用了

RouteDataValueProviderFactory
)。


0
投票

我遇到了同样的问题...你找到解决方案了吗?

© www.soinside.com 2019 - 2024. All rights reserved.