2012-03-30 30 views
24

Giới thiệu:JsonMaxLength ngoại lệ trên deserializing json lớn các đối tượng

ứng dụng Web, ASP.NET MVC 3, một hành động điều khiển chấp nhận một thể hiện của lớp mô hình POCO với (khả năng) lĩnh vực lớn.

lớp

mẫu:

public class View 
{ 
    [Required] 
    [RegularExpression(...)] 
    public object name { get; set; } 
    public object details { get; set; } 
    public object content { get; set; } // the problem field 
} 

điều khiển hành động:

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

Vấn đề:

Một hành động nên có thể chấp nhận đối tượng JSON lớn (ít nhất lên đến hàng trăm MB trong một yêu cầu duy nhất và đó không phải là trò đùa). Theo mặc định, tôi đã gặp một số hạn chế như httpRuntime maxRequestLength v.v. - tất cả được giải quyết trừ MaxJsonLengh - có nghĩa là ValueProviderFactory mặc định cho JSON không có khả năng xử lý các đối tượng đó.

Cố gắng:

Thiết

<system.web.extensions> 
    <scripting> 
     <webServices> 
     <jsonSerialization maxJsonLength="2147483647"/> 
     </webServices> 
    </scripting> 
    </system.web.extensions> 
  • không giúp.

Tạo tùy chỉnh của riêng tôi ValueProviderFactory như mô tả trong @ Darin của câu trả lời ở đây:

JsonValueProviderFactory throws "request too large"

  • cũng thất bại vì tôi không có khả năng sử dụng JSON.Net (vì lý do phi kỹ thuật) . Tôi đã cố gắng thực hiện chính xác deserialization ở đây bản thân mình nhưng dường như nó là một chút trên kiến ​​thức của tôi (chưa). Tôi đã có thể deserialize chuỗi JSON của tôi để Dictionary<String,Object> ở đây, nhưng đó không phải là những gì tôi muốn - Tôi muốn deserialize nó để các đối tượng POCO đáng yêu của tôi và sử dụng chúng như là tham số đầu vào cho hành động.

Vì vậy, các câu hỏi:

  1. Bất cứ ai biết cách nào tốt hơn để vượt qua những vấn đề mà không thực hiện tùy chỉnh phổ ValueProviderFactory?
  2. Có khả năng xác định bộ điều khiển và hành động cụ thể nào tôi muốn sử dụng ValueProviderFactory tùy chỉnh của mình không? Nếu tôi biết hành động trước hơn tôi sẽ có thể deserialize JSON để POCO mà không cần mã hóa nhiều trong ValueProviderFactory ...
  3. Tôi cũng đang suy nghĩ về việc triển khai ActionFilter tùy chỉnh cho vấn đề cụ thể đó, nhưng tôi nghĩ nó hơi xấu xí.

Bất kỳ ai cũng có thể đề xuất giải pháp tốt?

Trả lời

58

JsonValueProviderFactory tích hợp bỏ qua cài đặt <jsonSerialization maxJsonLength="50000000"/>.Vì vậy, bạn có thể viết một nhà máy tùy chỉnh bằng cách sử dụng được xây dựng trong thực hiện:

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

Việc sửa đổi duy nhất mà tôi đã so với nhà máy mặc định được thêm dòng sau:

serializer.MaxJsonLength = 2147483647; 

Đáng tiếc là nhà máy này không phải là mở rộng ở tất cả, niêm phong công cụ vì vậy tôi đã phải tạo lại nó.

và trong Application_Start của bạn:

ValueProviderFactories.Factories.Remove(ValueProviderFactories.Factories.OfType<System.Web.Mvc.JsonValueProviderFactory>().FirstOrDefault()); 
ValueProviderFactories.Factories.Add(new MyJsonValueProviderFactory()); 
+2

Có rất nhiều người đang làm việc về chủ đề này và đây là giải pháp duy nhất tôi có thể tìm thấy đã hoạt động trong Ứng dụng MVC4 của mình. Cảm ơn bạn! – stitz

+2

Siêu! Hoạt động tuyệt vời để Ràng buộc đối tượng Json lớn. Đối với các yêu cầu GET với đối tượng Json lớn, tôi đang sử dụng lớp ở đây: http://brianreiter.org/2011/01/03/custom-jsonresult-class-for-asp-net-mvc-to-avoid-maxjsonlength-exceeded -exception/ –

+2

Nếu bạn gặp sự cố khi đăng cấu trúc Json lớn qua bài đăng ajax lên Bộ điều khiển MVC4, hãy thử điều này trước bất kỳ nội dung nào khác. Cố gắng rất nhiều aproaches khác không có may mắn và điều này một mình đã lưu trong tuần của tôi. Cảm ơn rất nhiều @DarinDimitrov! –

16

tôi thấy rằng các maxRequestLength không giải quyết vấn đề tuy nhiên. Tôi đã giải quyết vấn đề của mình với cài đặt bên dưới. Nó là sạch hơn vì phải thực hiện một tùy chỉnh ValueProviderFactory

<appSettings> 
    <add key="aspnet:MaxJsonDeserializerMembers" value="150000" /> 
</appSettings> 

tín dụng đi vào các câu hỏi sau:

JsonValueProviderFactory throws "request too large"

Getting "The JSON request was too large to be deserialized"

Thiết lập này rõ ràng liên quan đến một mô hình json rất phức tạp và không phải kích thước thực.

+2

Điều này có thể hữu ích cho ai đó, mặc dù vấn đề ban đầu của tôi không bị ảnh hưởng bởi cài đặt này. Bạn đã có một tài liệu JSON rất phức tạp với rất nhiều phần tử - vì vậy cài đặt đó đã giúp bạn - và tôi có một tài liệu khá đơn giản với nội dung được mã hóa lớn cho một số giá trị. –

+0

Bạn có quyền 100%. Tôi đã sửa đổi câu trả lời của tôi như vậy. – Oliver

+0

Oliver - Bạn nhấn tối đa # mục trong từ điển json thay vì độ dài nội dung hoặc độ phức tạp. Có giới hạn 1000 mục theo mặc định trong JavaScriptSerializer. Câu trả lời của bạn là đúng cho kịch bản này nhưng đây là liên kết về chủ đề https://msdn.microsoft.com/en-us/library/hh975440.aspx –

3

Các giải pháp của Darin Dimitrov làm việc cho tôi, nhưng tôi cần phải thiết lập lại vị trí của con suối trong những yêu cầu trước khi đọc nó, thêm dòng này:

controllerContext.HttpContext.Request.InputStream.Position = 0;

Vì vậy, bây giờ, phương pháp GetDeserializedObject trông như thế này :

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; 
    } 
Các vấn đề liên quan