AJAX中使用GB2312的编码时出现乱码

作者: Robin 分类: Asp.net 发布时间: 2009-07-10 10:23

在使用ASP.NET AJAX Control Tookit 1.0.10618的时候, 发现如果在Web.config中如下设置使用gb2312编码:
<globalization
fileEncoding=”gb2312″ requestEncoding=”gb2312″
responseEncoding=”gb2312″ culture=”zh-CN” uiCulture=”zh-CN”/>
  AJAX控件无法正常使用,错误为脚本库中出现的,提示为:
  “出现了运行时间错误。是否要进行调试?行: 684 错误: 缺少’}’”
  “行: 86 错误: ‘AjaxControlToolkit’未定义”
 
 经过搜索与试验,最终比较圆满的解决了这个问题,方案也找到了两种,比较完美的解决了ASP.NET AJAX Control Tookit
1.0.10618的乱码问题。本文记录下我解决这个问题的过程,有一点罗嗦,如果你只想知道结果的话,那就直接看文中强调的部分吧。

  我首先在网上搜了一下,发现CSDN也有提类似问题的帖子,但是除了将编码改为utf-8之外没有其他的解决方案,而且还有人质疑为什么一定要用gb2312的编码。在我看来,使用gb2312可能有以下理由:
  1、接收从其他页面发过来的GB2312编码的URL参数。虽然在URL中直接使用GB2312编码并不标准,但是目前相当多的页面都是直接通过GB2312来在URL中传递中文参数的。
  2、对网页的Referrer进行访问来源统计的时候,很多网站的Referrer都是用GB2312编码的,页面用UTF-8的话无法得到含有中文(比如用户进行搜索的关键字)的真实的Referrer。
  3、提交到其他使用GB2312的网页。比较典型的应用是在线支付网关,大多数在线支付网关都是只支持GB2312编码,如果你使用UTF-8提交内容就会产生乱码。
  4、在增加AJAX功能之前网站一直使用GB2312,如果改编码会牵扯以前项目中很多的方面。
  5、减少网络流量。因为GB2312用固定的两个字节存储中文,而UTF-8是不定长的编码,每一个汉字需要2-4个字节。

  经过尝试,我发现在ASP.NET AJAX Control
Toolkit中提供的示例网站上即使使用了GB2312编码,也可以正常使用。这时<globalization>标签中只有
culture=”zh-CN” uiCulture=”zh-CN”这两个参数不一样,在示例网站中是culture=”en-us”
uiCulture=”en”。在我的网站中,把uiCulture也改为”en”之后,AJAX Control
Toolkit可以正常运行了。这样就得到了解决乱码问题的第一个方法:

方案一:将Web.config文件中<globalization>的uiCulture=”zh-CN”改为uiCulture=”en”。

  经过使用Fiddler对修改前后的脚本进行比较,发现这个参数对网站实际的影响表现在AJAX Control Toolkit的国际化方面,参数值en所对应的输出片段为:
AjaxControlToolkit.Resources={
“PasswordStrength_InvalidWeightingRatios”:”Strength
Weighting ratios must have 4
elements”,”Animation_ChildrenNotAllowed”:”AjaxControlToolkit.Animation.createAnimation
cannot add child animations to type \”{0}\” that does not derive from
AjaxControlToolkit.Animation.ParentAnimation”,……
  参数zh-CN的输出中相对应的片段为:
AjaxControlToolkit.Resources={
“PasswordStrength_InvalidWeightingRatios”:
“密码强度的权重比例必须有 4
种”,”Animation_ChildrenNotAllowed”:”AjaxControlToolkit.Animation.createAnimation
无法加入一个不是派生自 AjaxControlToolkit.Animation.ParentAnimation 且类型为 {0}
的子动画”,……
  虽然经过简单的修改,避开了中文的编码问题,脚本不再报错了,可这样做局限性
很明显:这个方案只能用在没有国际化的网站中;而且要可以接受可能出现的英文提示。另外在类似Login、CreateUserWizard等控件中会自
动根据uiCulture这个参数生成对应语言的界面,修改后会都变成英文。如果整个网站中这样的页面不是很多,可以在<%@ Page
%>中加上一个UICulture=”zh-CN”来单独为某一些页面配置UICulture而不影响全局。也可以将个别控件的样式、模板全部自定
义,而不让其自动生成。

  方案一十分简单,只是局限性太明显了,有没有更好的办法呢?由于AJAX Control
Toolkit是一个开源项目,如果问题确实出在这里面,就给了我们自己修改Bug的机会。在下手之前我们需要知道使用UTF-8和GB2312编码的输
出之间有哪里不一样,才能初步判断问题出在哪里。
  查看GB2312的输出中未经解码的十六进制Response的内容,这一段内容如下:

 
 一眼看去,选定部分所占用的字节数肯定超过了”密码强度的权重比例必须有 4
种”这个字符串字数的两倍,似乎这并不是GB2312的编码。经过确认,这一段代码果然是UTF-8编码的!很明显问题就在这里,Web.config明
明选择的是GB2312,输出的脚本文件中的中文编码却是UTF-8,不出乱码才怪呢。可是将Web.config中的responseEncoding
改为”UTF-8″之后,输出的中文也是用UTF-8编码的,内容一模一样:

  经过使用文件比较工具Beyond Compare对比后,发现其他的不同之处不会引起错误,于是可以断定问题就在这里。
 
 看到这里,相信跟Unicode打过交道的朋友们已经可以猜到,很可能是AJAX Control
Toolkit在输出脚本的时候没有对当前所使用的编码进行判断,而统一使用了在.NET中作为默认值的UTF-8。有了这个假设,我们就可以在AJAX
Control Toolkit的源代码中进行求证,确认后就可以尝试通过修改源代码来解决这个问题。
  输出脚本是ToolkitScriptManager控件的责任,在ToolkitScriptManager.cs文件中发现了一个可疑的方法:
private static void WriteScripts(List<ScriptEntry> scriptEntries, TextWriter outputWriter)
 
 这个方法通过调用System.Resources.ResourceManager来输出脚本资源,在项目的资源文件中也发现了”密码强度的权重比例
必须有 4
种”等字符串,只是这个方法内部没有任何与编码有关的代码,而编码的设置应该在参数outputWriter传入这个方法之前就已经确定了,如果只修改这
个方法中outputWriter的编码,恐怕会使这个方法前面输出的脚本产生混乱。于是继续寻找调用WriteScripts的方法,找到了最可疑的方
法为:
public static bool OutputCombinedScriptFile(HttpContext context)
  在方法体中,文件的第256行发现了生成传入WriteScripts的参数:
 using (StreamWriter outputWriter = new StreamWriter(outputStream))
 
 有经验的话一眼就能看出问题,StreamWriter的默认编码为UTF-8,如果需要指定编码的话需要调用另一个重载的构造函数,将编码作为第二个
参数传递给StreamWriter的构造函数。而这里确不管当前使用了哪一种编码,统统改成UTF-8来输出,正会造成前面所说的结果。
  这个问题的修改很简单,因为我们可以通过OutputCombinedScriptFile这个方法传入的参数HttpContext来获得所使用的编码,并把他加到StreamWriter构造函数的第二个参数上去就可以了:
 using (StreamWriter outputWriter = new StreamWriter(outputStream, context.Response.ContentEncoding))
  编译并在自己的网站中更新引用,运行后发现的确已经解决了这个问题。看来不需要修改其他的地方了。这时再用Fiddler查看十六进制的Response输出:

  这下代码的长度确实缩短了,可以验证一下,这正是”密码强度的权重比例必须有 4 种”的GB2312编码。总结一下方案二:

方案二:修改AJAX Control Toolkit
1.0.10618的源代码,将ToolkitScriptManager.cs的第256行改为using (StreamWriter
outputWriter = new StreamWriter(outputStream,
context.Response.ContentEncoding)),并重新编译。

  至此,ASP.NET AJAX Control Tookit
1.0.10618与GB2312的乱码问题似乎已经完全解决了。只是我在测试的时候,发现在UpdatePanel中提交的文本框中的中文仍然是乱码,
说明ASP.NET AJAX Extensions
1.0本身仍然有编码的问题。只是我的网站中使用UpdatePanel的页面都不需要跟其他的GB2312网页直接交互,所以我就把这些页面的编码单独
改成了UTF-8,而其他大部分页面使用在Web.config中默认的GB2312编码。这里多说一句,在<%@
Page%>中只能设置ResponseEncoding,无法设置RequestEncoding,而解决这个问题的关键是设置
RequestEncoding。可以通过Web.config的<location>标签中针对某一个文件设置其<
globalication>标签的requestEncoding属性,如:
<configuration>
 <location path=”Member/UserProfile.aspx”>
  <system.web>
   <globalization requestEncoding=”utf-8″/>
  </system.web>
 </location>
</configuration>

  比较一下这两个方案,方案一优点是简单,只需要修改一个属性,缺点是不能用在国际化的网站上,而且要能接收可能会出现的英文提示。方案二缺点是
需要修改并重新编译Control
Toolkit,优点是解决的比较彻底,没有局限性。与方案二的有点相比,其缺点似乎是微不足道的,因为只需要重新编译一次,一劳永逸,且步骤并不复杂。
最后留了一个小尾巴,就是ASP.NET AJAX Extensions
1.0的乱码问题,可以通过设置个别页面的编码来绕过,暂时我是没有去解决的动力了,不过它也提供了源代码,感兴趣的朋友们可以尝试一下。

   方案二修改后的ASP.NET AJAX Control Toolkit
1.0.10618编译我已经上传到了CSDN的下载栏目,基于1.0.10618的代码,只修改了本文说的那一行代码,版本号和签名都没有变,因此可以
直接替换项目中以前用的1.0.10618版本。下载地址:http://download.csdn.net/source/232485
  本文所提到的Bug我也已经提交到AJAX Control Toolkit官方网站的Issue Tracker栏目,如果你也遇到了类似的问题,不妨来投一票,以便引起开发者的重视,也许在下一个官方版本就会解决了。地址:http://www.codeplex.com/AtlasControlToolkit/WorkItem/View.aspx?WorkItemId=12327

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!

发表评论

电子邮件地址不会被公开。 必填项已用*标注

标签云