2012-03-30 60 views
24

簡介:JsonMaxLength異常對象

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"

  • 也失敗了,因爲我沒有可能使用(由於非技術原因)JSON.Net 。我試圖在這裏自己實現正確的反序列化,但顯然它有點超出我的知識(還)。我能夠在這裏反序列化我的JSON字符串到Dictionary<String,Object>,但那不是我想要的 - 我想將它反序列化爲我可愛的POCO對象,並將它們用作動作的輸入參數。

所以,問題:

  1. 任何人都知道更好的方法來解決問題,而無需實現普遍的風俗ValueProviderFactory?
  2. 是否有可能指定什麼特定的控制器和操作我想使用我的自定義ValueProviderFactory?如果我事先知道這個動作比我能夠將JSON反序列化爲POCO而不需要在ValueProviderFactory中編寫很多代碼...我也在考慮爲該特定問題實現自定義ActionFilter,但我認爲它有點難看。

任何人都可以提出一個好的解決方案?

回答

58

內置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()); 
+2

有很多人關於這個主題的任務,這是我能找到的唯一解決方案,在我的MVC4應用程序工作。謝謝! – stitz 2013-02-23 11:41:14

+2

超級!適用於綁定大型Json對象。對於具有大型Json對象的GET請求,我在此處使用該類:http://brianreiter.org/2011/01/03/custom-jsonresult-class-for-asp-net-mvc-to-avoid-maxjsonlength-exceeded -exception/ – 2013-03-20 10:28:00

+2

如果您無法在MVC4控制器上發佈ajax文章發佈大型Json結構時遇到問題,請在任何其他內容之前嘗試執行此操作。試了很多其他的竅門,沒有運氣,這一個人節省了我的一週。非常感謝@DarinDimitrov! – 2014-02-17 13:04:22

16

我發現了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

這可能是有用的人,雖然我原來的問題不受此設置的影響。你有一個非常複雜的JSON文檔,其中包含很多元素 - 所以這個設置對你有幫助 - 而且我有一些非常簡單的文檔,其中一些值的編碼內容很大。 – 2013-08-23 22:00:44

+0

你是100%的權利。我已經修改了我的答案。 – Oliver 2013-08-25 10:01:40

+0

Oliver - 您擊中了json字典中的最大項目數量,而不是內容長度或複雜度。 JavaScriptSerializer默認有1000項限制。對於這種情況,您的回答是正確的,但這裏是主題上的鏈接https://msdn.microsoft.com/zh-cn/library/hh975440.aspx – 2015-02-02 11:48:10

3

達林季米特洛夫的解決方案對我的作品,但我需要之前重置請求流的位置讀它,加入這一行:

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; 
    }