首页 > 疑难杂症 > Asp.Net页面的编码问题

Asp.Net页面的编码问题

疑难杂症 , ,

莫名其妙的问题

这些天,一个客户的网站遇到了一些莫名其妙的问题。先是首页在Firefox下看正常,但是在IE上看,同个样式表文件,却有部分样式没能加载,导致导航菜单没有背景并且走位了。后来样式的问题解决了,但是又出现了一个页面上,部分文字乱码部分正常的诡异问题。

字符编码导致的样式文件解析错误

对于第一个问题我的第一反应是CSS样式表中部分样式存在不兼容性。但是检查了导航菜单样式的写法,没有看出来有什么特别的,都是常见的属性常见的值。但是用IE自带的开发者工具看了一下出问题的元素的样式,发现和在Firebug中看到的样式居然不同。后来无意中在HTTP Watch中看了style.css文件的内容,发现注释有乱码,当即联想到以前写过的文章《文档字符集导致的脚本错误》。当一个文档(HTML文档或是CSS文档)的文件存储字符集和浏览器解码使用的字符集不一致的时候会出现类似的问题,特别是utf-8编码过的文档却以gb2312去解码的时候,出现的问题就更诡异了。

于是我在IE上改用UTF-8去解析网页,结果果然,导航菜单的样式正常了,背景图片也正常加载了(虽然其他地方出现了一些乱码情况)。这证明了我的思路是对的。但是具体是哪个地方出了问题呢,为什么Firefox、Opera和Chrome下都没问题,偏偏就IE有问题呢?

我又仔细查看了客户给出的网页和样式文件,又分析了相关的HTTP消息,发现请求.aspx文件(出问题的网页文档)的时候,服务端返回的Content-Type是text/html;charset=gb2312,那么所有浏览器自然会以服务器返回的字符集来解析文档,这个没问题,但是当网页中引用了外部资源的时候(在这里是css样式表),IE是使用当前网页的文档字符集去解析此外部资源,除非外部资源声明了编码格式(HTTP响应头中声明或是在文档最前方使用@charset声明),而Firefox、Opera和Chrome在CSS文件没有显式声明字符集的情况下,始终是以UTF-8来解码的。

问题既然和样式相关,也就是说在使用gb2312解析该样式表的时候出现了问题,那可以猜到,样式表文件实际的存储格式是utf-8的。

为了找到具体出问题的地方,我用Notepad++——貌似手头只有这软件可以自由编解码——模拟了一下使用gb2312解码此utf-8格式的样式表的场景,发现了其中一段出问题的样式,UTF-8解码后很正常,因为本身就是UTF-8编码的:

/*新导航*/
#new_nav ul{ width:672px; height:33px; margin:0px; padding:0px;  }

当使用gb2312解码之后得到如下:

/*鏂板鑸?/
#new_nav ul{ width:672px; height:33px; margin:0px; padding:0px;}

很明显的,下面的样式都被注释掉了。自然一切问题都得以解释了。

解决办法很多种,但目标都是一个,保证文档声明和实际字符集的一致。对于CSS样式表,我们可以在文件头显式设置@charset ‘XXX’来声明字符集。或者在使用link引用外部样式表的时候显式加上charset属性。

另外,如果所有文件都能保存为utf-8 with signature的话,也就是带BOM标记的UTF-8编码格式,那么所有浏览器都能自识别,而不会产生识别错误的问题。

Asp.Net中涉及到的编码问题

第二个问题虽然我知道还是编解码的环节出了问题,但还是分析了我半天才找到问题所在。

测试环境简化

为了简单,我把第二个问题中涉及到的页面简化成如下两个文件:

Default.aspx文件:

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
</head>
<body>
    <p><%= Msg %></p>
    <p>
        Aspx页面中的中文
    </p>
</body>
</html>

Default.aspx.cs后台代码文件:

using System;
public partial class _Default : System.Web.UI.Page 
{
    public String Msg = "代码中的中文";
    protected void Page_Load(object sender, EventArgs e)
    {
    }
}

两个文件非常简单,从aspx文件中输出一段文字,从cs文件中输出一段文件。

Asp.Net页面涉及到的编解码环节

为了理解第二个问题,我们首先先来看一下在Asp.Net中一个页面究竟会涉及到什么编码环节。

1. 文件本身存储使用的编码(在Visual Studio中新建文件的默认编码是UTF-8 With BOM)

2. 文件被Asp.Net引擎转换处理时使用的编码

3. 文件写到HTTP响应流时使用的编码

4. 文件在HTTP头声明的编码(Content-Type=”text/html;charset=xxx”)

对于第一个环节就不需要说了,直接“Advanced Save As..”然后选择相应编码即可。对于2、3、4环节中涉及到的编码,asp.Net提供了全局配置还有页面级配置两种方式。

在web.config文件中可以管理全局编码配置。

<configuration>
    <system.web>
        <globalization responseEncoding="utf-8" fileEncoding="utf-8" />
    </system.web>
</configuration>

在system.web一节下添加globalization元素,它有几个可配置选项,具体参见MSDN。其中有两个属性和此次的问题相关,一个是fileEncoding(注意区分大小写),这个配置项决定了asp.net引擎在将aspx文件以及cs文件合并成html文档时使用的编码。如果aspx或者cs文件的格式和此属性配置的不一致,那么就有可能导致生成的html文档出现乱码。默认情况下,该属性的值为ANSI编码,也就是说中文系统下为GB2312。但是如果文档本身存储格式是UTF-8 with BOM,那么asp.net将始终以UTF-8编码来解析aspx和cs文件。这也是为什么默认情况下什么都不需要配置的缘故。

另外一个属性是responseEncoding,这个配置决定了上述的第3、4环节,也就是当asp.net生成好html文档之后,将其写入HTTP响应正文时使用的编码,同时会设置HTTP的Content-Type响应头中的charset域。如果前面的环节没有问题,html生成正常的话,那么这里无论设置什么编码浏览器均会正常解码,页面上都不会有乱码。但是如果前面的步骤出了问题,那自然就会有乱码产生了。

注意上面4个环节我分别用了两种颜色来标明,看了上面的解释之后你就很明白了,1和2环节编码需一致,否则中间生成的html文档就已经有乱码了。3和4环节的编码须一致,否则浏览器显示不正常。

客户网站出现的部分文字乱码问题的原因就是因为aspx文件存储的格式是不带BOM标记的UTF-8(天知道他是怎么给整成这个编码的)而CS文件没有改动过,还是UTF-8 with BOM,而全局的fileEncoding没有配置,因此在第二个环节的时候使用gb2312去解析一个utf-8的文档,自然有问题,因此aspx文件中的中文显示为乱码,而cs文件中指定的中文正常显示。

上面介绍的是asp.net中的全局编码配置,其实针对每个页面还可以进行特殊配置。例如在aspx页面中的Page指令中可以添加ResponseEncoding属性。

image

或者我们可以在后台C#代码中利用Response对象。

Response对象提供了两个和字符集编码相关的属性,Response.Charset和Response.ContentEncoding。这两个有什么区别呢?ContentEncoding和web.config中配置的responseEncoding是等价的,都是既影响HTTP响应流编码又影响Content-Type响应头,而Charset影响的只是响应头Content-Type而已。 将其设置为null的话,Content-Type中就不会有charset域了。

——Kevin Yang

本博客遵循CC协议2.5,即署名-非商业性使用-相同方式共享
写作很辛苦,转载请注明作者以及原文链接~
如果你喜欢我的文章,你可以订阅我的博客:-D点击订阅我的文章

  1. X﹏X 到现在还没有评论~
  1. 暂时没有trackbacks.