今日碰到了這麼一個異常,異常資訊如下:
Type : System.InvalidOperationException, mscorlib, Version=2.0.0.0,Culture=neutral, PublicKeyToken=b77a5c561934e089Message : 使用 JSON JavaScriptSerializer 進行序列化或還原序列化時出錯。字串的長度超過了為 maxJsonLength 屬性設定的值。Source : System.Web.ExtensionsHelp link : Data : System.Collections.ListDictionaryInternalTargetSite : Void Serialize(System.Object, System.Text.StringBuilder,SerializationFormat)Stack Trace : 在System.Web.Script.Serialization.JavaScriptSerializer.Serialize(Object obj,StringBuilder output, SerializationFormat serializationFormat) 在 System.Web.Script.Serialization.JavaScriptSerializer.Serialize(Objectobj, SerializationFormat serializationFormat) 在 System.Web.Script.Serialization.JavaScriptSerializer.Serialize(Object
這個異常是在執行MVC中的JsonResult的時拋出的,根據異常的Message得知是序列化的字串超出了maxJsonLength的限制。並得知這個屬性是由JavaScriptSerializer提供的,因為MVC內建的JsonResult是用JavaScriptSerializer進行序列化的。在網上快速搜尋了一下,碰到這個問題的童鞋不少,大部分推薦的解決的方法都是在web.config中加入以下配置,設定maxJsonLength的長度即可。
View Code
<system.web.extensions>
<scripting>
<webServices>
<jsonSerialization maxJsonLength="20971520"/>
</webServices>
</scripting>
</system.web.extensions>
在自動提示下,很順暢的加上了幾行代碼,以為這是個簡潔的解決方案,但是運行網站之後報以下錯誤:
分析器錯誤訊息: 無法識別的配置節 system.web.extensions。
這似乎又是碰到了一家人不認識的情況,既然無法識別為什麼能帶智能感知的方式輸出,而且是已system.web開頭的,按道理不會識別不出的。以為是拼字錯誤,經過進一步搜尋之後,原來是缺乏了聲明,加上對節點的聲明即可(很大一串的內容)。
View Code
<sectionGroup name="system.web.extensions" type="System.Web.Configuration.SystemWebExtensionsSectionGroup, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
<sectionGroup name="scripting" type="System.Web.Configuration.ScriptingSectionGroup, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
<section name="scriptResourceHandler" type="System.Web.Configuration.ScriptingScriptResourceHandlerSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="MachineToApplication"/>
<sectionGroup name="webServices" type="System.Web.Configuration.ScriptingWebServicesSectionGroup, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
<section name="jsonSerialization" type="System.Web.Configuration.ScriptingJsonSerializationSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="Everywhere"/>
<section name="profileService" type="System.Web.Configuration.ScriptingProfileServiceSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="MachineToApplication"/>
<section name="authenticationService" type="System.Web.Configuration.ScriptingAuthenticationServiceSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="MachineToApplication"/>
<section name="roleService" type="System.Web.Configuration.ScriptingRoleServiceSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="MachineToApplication"/>
</sectionGroup>
</sectionGroup>
</sectionGroup>
加入了聲明之後,運行正常,但是問題依舊還在,而且不管maxJsonLength設定成多大都無效,就算改成1個字元,居然還能跑起來。碰到這個問題只能進一步的搜尋。在這篇文章中找到了原委http://weblogs.asp.net/rashid/archive/2009/03/23/submitting-my-first-bug-after-asp-net-mvc-1-0-rtm-release.aspx。
原來MVC架構內建的JsonResult代碼中,在使用JavaScriptSerializer時,都是採用的預設值,沒有從maxJsonLength讀取值,即忽略了這個配置。要想使用配置中的值,只能自訂一個JsonResult,重寫原JsonResult的ExecuteResult方法,於是定義一個ConfigurableJsonResult,代碼如下:
ConfigurableJsonResult
1 public class ConfigurableJsonResult : JsonResult
2 {
3 public override void ExecuteResult(ControllerContext context)
4 {
5 if (context == null)
6 {
7 throw new ArgumentNullException("context");
8 }
9 if (JsonRequestBehavior == JsonRequestBehavior.DenyGet &&
10 String.Equals(context.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase))
11 {
12 throw new InvalidOperationException("This request has been blocked because sensitive information could be disclosed to third party web sites when this is used in a GET request. To allow GET requests, set JsonRequestBehavior to AllowGet.");
13 }
14
15 HttpResponseBase response = context.HttpContext.Response;
16
17 if (!String.IsNullOrEmpty(ContentType))
18 {
19 response.ContentType = ContentType;
20 }
21 else
22 {
23 response.ContentType = "application/json";
24 }
25 if (ContentEncoding != null)
26 {
27 response.ContentEncoding = ContentEncoding;
28 }
29 if (Data != null)
30 {
31 JavaScriptSerializer serializer = new JavaScriptSerializer();
32
33 ScriptingJsonSerializationSection section = ConfigurationManager.GetSection("system.web.extensions/scripting/webServices/jsonSerialization") as ScriptingJsonSerializationSection;
34
35 if (section != null)
36 {
37 serializer.MaxJsonLength = section.MaxJsonLength;
38 serializer.RecursionLimit = section.RecursionLimit;
39 }
40
41 response.Write(serializer.Serialize(Data));
42 }
43 }
44 }
關鍵在紅色標記的代碼,讀取配置的值。JavaScriptSerializer還有其他屬性可配置,沒有列出與實現,暫時夠用。
這樣在返回長字元內容的json結果時,直接替換原JsonResult即可,同時也兼顧了可配置的靈活性。