说明:
Web应用程序,ASP.NET MVC 3,一种控制器操作,它接受具有(可能)大字段的POCO模型类的实例。
模特课:
public class View
{
[Required]
[RegularExpression(...)]
public object name { get; set; }
public object details { get; set; }
public object content { get; set; } // the problem field
}
控制器操作:
[ActionName(...)]
[Authorize(...)]
[HttpPost]
public ActionResult CreateView(View view)
{
if (!ModelState.IsValid) { return /*some ActionResult here*/;}
... //do other stuff, create object in db etc. return valid result
}
问题:
一个动作应该能够接受大型JSON对象(在一个请求中至少高达百兆字节,这不是开玩笑)。默认情况下,我遇到了一些限制,如httpRuntime maxRequestLength
等 - 除了MaxJsonLengh之外都解决了 - 这意味着JSON的默认ValueProviderFactory无法处理这些对象。
尝试:
设置
<system.web.extensions>
<scripting>
<webServices>
<jsonSerialization maxJsonLength="2147483647"/>
</webServices>
</scripting>
</system.web.extensions>
创建我自己的自定义ValueProviderFactory,如@ Darin的回答所述:
JsonValueProviderFactory throws "request too large"
Dictionary<String,Object>
,但这不是我想要的 - 我想将它反序列化为我可爱的POCO对象并将它们用作操作的输入参数。所以,问题:
任何人都可以建议一个好的解决方案吗?
答案 0 :(得分:64)
内置的JsonValueProviderFactory忽略<jsonSerialization maxJsonLength="50000000"/>
设置。因此,您可以使用内置实现编写自定义工厂:
public sealed class MyJsonValueProviderFactory : ValueProviderFactory
{
private static void AddToBackingStore(Dictionary<string, object> backingStore, string prefix, object value)
{
IDictionary<string, object> d = value as IDictionary<string, object>;
if (d != null)
{
foreach (KeyValuePair<string, object> entry in d)
{
AddToBackingStore(backingStore, MakePropertyKey(prefix, entry.Key), entry.Value);
}
return;
}
IList l = value as IList;
if (l != null)
{
for (int i = 0; i < l.Count; i++)
{
AddToBackingStore(backingStore, MakeArrayKey(prefix, i), l[i]);
}
return;
}
// primitive
backingStore[prefix] = value;
}
private static object GetDeserializedObject(ControllerContext controllerContext)
{
if (!controllerContext.HttpContext.Request.ContentType.StartsWith("application/json", StringComparison.OrdinalIgnoreCase))
{
// not JSON request
return null;
}
StreamReader reader = new StreamReader(controllerContext.HttpContext.Request.InputStream);
string bodyText = reader.ReadToEnd();
if (String.IsNullOrEmpty(bodyText))
{
// no JSON data
return null;
}
JavaScriptSerializer serializer = new JavaScriptSerializer();
serializer.MaxJsonLength = 2147483647;
object jsonData = serializer.DeserializeObject(bodyText);
return jsonData;
}
public override IValueProvider GetValueProvider(ControllerContext controllerContext)
{
if (controllerContext == null)
{
throw new ArgumentNullException("controllerContext");
}
object jsonData = GetDeserializedObject(controllerContext);
if (jsonData == null)
{
return null;
}
Dictionary<string, object> backingStore = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
AddToBackingStore(backingStore, String.Empty, jsonData);
return new DictionaryValueProvider<object>(backingStore, CultureInfo.CurrentCulture);
}
private static string MakeArrayKey(string prefix, int index)
{
return prefix + "[" + index.ToString(CultureInfo.InvariantCulture) + "]";
}
private static string MakePropertyKey(string prefix, string propertyName)
{
return (String.IsNullOrEmpty(prefix)) ? propertyName : prefix + "." + propertyName;
}
}
与默认工厂相比,我做的唯一修改是添加以下行:
serializer.MaxJsonLength = 2147483647;
不幸的是,这个工厂根本不可扩展,密封的东西所以我不得不重新创建它。
和您的Application_Start
:
ValueProviderFactories.Factories.Remove(ValueProviderFactories.Factories.OfType<System.Web.Mvc.JsonValueProviderFactory>().FirstOrDefault());
ValueProviderFactories.Factories.Add(new MyJsonValueProviderFactory());
答案 1 :(得分:17)
我发现maxRequestLength并没有解决问题。 我用以下设置解决了我的问题。它比实现自定义 ValueProviderFactory
更清晰<appSettings>
<add key="aspnet:MaxJsonDeserializerMembers" value="150000" />
</appSettings>
归功于以下问题:
JsonValueProviderFactory throws "request too large"
Getting "The JSON request was too large to be deserialized"
此设置显然与高度复杂的json模型有关,而与实际大小无关。
答案 2 :(得分:4)
Darin Dimitrov的解决方案对我有用,但我需要在读取之前重置请求流的位置,添加以下行:
controllerContext.HttpContext.Request.InputStream.Position = 0;
现在,方法GetDeserializedObject如下所示:
private static object GetDeserializedObject(ControllerContext controllerContext)
{
if (!controllerContext.HttpContext.Request.ContentType.StartsWith("application/json", StringComparison.OrdinalIgnoreCase))
{
// not JSON request
return null;
}
controllerContext.HttpContext.Request.InputStream.Position = 0;
StreamReader reader = new StreamReader(controllerContext.HttpContext.Request.InputStream);
string bodyText = reader.ReadToEnd();
if (String.IsNullOrEmpty(bodyText))
{
// no JSON data
return null;
}
JavaScriptSerializer serializer = new JavaScriptSerializer();
serializer.MaxJsonLength = 2147483647;
object jsonData = serializer.DeserializeObject(bodyText);
return jsonData;
}