您的位置:首页 > 其它

一次乱码问题解决过程反思

2010-12-16 10:52 459 查看
一个很简单的问题花了一整天的时间去解决,教训总结在这里,以后当引以为戒。

问题:服务器上的一个新闻发布网站在提交新闻后显示出来的新闻内容页面是乱码。不仅如此,以前发布过的所有新闻内容也都变成了乱码。老
师说问题这两天才出
现,以前一直运行良好。

当时习惯性地马上打开浏览器访问网站,发现网站在firefox中本身就是乱码,在浏览器菜单中选择了GBK编码看到了正常的网页。点
击新闻超链接,这时候弹出一个很酷的窗口,里面显示一堆乱码。

因为网站的使用者都是非技术用户,而且网站从上线到现在一直运行良好,自己最近也没有改过数据库,所以感觉不太可能是数据库的问题。

其他部分编码都正常后,只有弹出窗口中显示的新闻内容是乱码。查看了一下页面源代码,发现<a>标签上也
没有绑定onclick的处理事件。当时最要紧的事是先让以前发布的新闻能正常阅读。把服务器上的一个html文件download下来,发现在文件中用
meta标签指定了gbk编码,用iconv试着转换了一下(iconv --form=gbk
news.html)发现这个文件本身也是用gbk编码存储的。自己把文件另存为utf8编码(iconv --from=gbk news.html
--to=utf8 > news.utf8.html),并把meta标签也修改成了utf8(<meta
http-equiv="Content-Type"
content="text/html;charset=utf8">),放到服务器上去,然后点击这条新闻,发现乱码没了!于是把服务器
上的所有关于新闻内容的html文件都做了个备份,然后下载到本地,用脚本把它们全转换成utf8编码存储,并把其中的meta标签都做了修改,再上传到
服务器上,此刻以前发布的新闻都可以正常阅读了。将要发布的新闻也可以用这样的方法暂发布上去。(当时这么做完全是出于前几次乱码问题的经验)

接下来考虑怎么样修复网站,使得通过新闻提交页面提交的新闻点击时都能正常显示。<a>标签上并没有绑定
事件处理函数,而这个弹出窗口一定是javascript做的。用firebug查看,发现页面加载了prototype.js和lightbox.js
等几个javascript文件。自己只是略懂javascript的皮毛,浏览了一下prototype.js后,放弃了读懂它然后解决问题的打算。

回过头来发现一开始自己打开网站主页面时也是乱码,通过浏览器菜单选择编码才正常。点击跳转到提交新闻页面开始也是乱码。弹出窗口中的
乱码也是修改了meta标签中的charset为utf8才正常。网站主页面上明明是用meta来说明过它自己是gb2312编码的,firefox一开
始也没有用utf8编码去解释它。这让自己非常郁闷。同时,自己看到网站主页面上的html头部代码是:

<HTML lang=gb2312
xmlns="http://www.w3.org/1999/xhtml"><HEAD><TITLE>��������</TITLE>

<META http-equiv=Content-Type content="text/html;
charset=gb2312">

<META http-equiv=Content-Language content=UTF-8>


使用了html的lang属性,meta中的Content-Type,
Content-Language三个地方都指定了编码。这让自己越发对HTML和HTTP的相关标准不自信,于是希望能彻底弄明白这些相关标准。在
w3c和unicode网站上搜索了一堆标准文档回来,希望能一劳永逸。

事后回想,发现这根本没有必要。到这一步,自己的思路总结起来其实就是在怀疑乱码的出现是因为服务端页面、浏览器、与此次通信相关的前
后台代码,它们没有清楚地表达自己本身的编码或者没有正确地按照指定的编码解释。再往回看看,自己之所以这么怀疑,是因为自己比较有把握地排除了数据库的
因素。所以自己的思路总结起来可以说,问题要么出现在数据库和web服务器之间,要么出现在web服务器和浏览器之间。而根据自己的考虑,前者是可以排除
的。问题很可能就在web服务器和浏览器之间,因为浏览器是久经考验的工具,所以再细分一步,问题极可能就出现在web服务器上的相关代码没有清楚地向浏
览器表达自己的编码方式。那么这个时候,要么检查后台代码和相关javascript代码,要么从现象入手,调试通信过程,定位到具体的错误环节。这样一
比较,自然后者较前者有效率。如果思路能走到这一步,那离解决问题也就不远了。自己却偏偏在这里偏离了问题,南辕北辙。这是第一个教训。

看了一会儿文档,想起来问题的紧迫,还剩下很多文档要看,感到一阵恐慌。自己在决定看文档前并没有冷静地估计这对解决问题有什么好处,
会花费多少时间,有没有这个必要,中间可能会出现哪些问题。关键是要解决眼前的问题,至于根本上解决问题,怎么样去解决,有没有解决的必要,有没有必要自
己去解决,都不应该在这个时候考虑。

这样,一个上午的时间过去了。吃饭的时候冷静了一下。下午先从调试通信过程入手,马上发现,点击超链接后发生了一次ajax调用,下面
是用httpfox抓取到的HTTP request header:

POST article/2010/11/1291112086.html
HTTP/1.1

...

X-Requested-With    XMLHttpRequest

X-Prototype-Version    1.4.0

Content-Type
application/x-www-form-urlencoded; charset=UTF-8

...

[/code]

其中post的请求对象是一个html文件,一般都应该是一个CGI脚本,这一点让人有点困惑。网页上加载了lightbox.js,
prototype.js等几个js脚本,浏览了一下这几个文件,发现相关的代码段应该是:

// Begin Ajax request based off of the href of the clicked linked

loadInfo: function() {

var myAjax = new Ajax.Request(

this.content,

{method: 'post', parameters: '', onComplete:
this.processInfo.bindAsEventListener(this)}

);

},

// Display Ajax response

processInfo: function(response){

info = "<div id='lbContent'>" + response.responseText +
"</div>";

new Insertion.Before($('lbLoadMessage'), info)

$('lightbox').className = "done";

this.actions();

},

// Search through new links within the lightbox, and attach click event

actions: function(){

lbActions = document.getElementsByClassName('lbAction');

for(i = 0; i < lbActions.length; i++) {

Event.observe(lbActions[i], 'click',
this[lbActions[i].rel].bindAsEventListener(this), false);

lbActions[i].onclick = function(){return false;};

}

},

// Onload, make all links that need to trigger a lightbox active

function initialize(){

addLightboxMarkup();

lbox = document.getElementsByClassName('lbOn');

for(i = 0; i < lbox.length; i++) {

valid = new lightbox(lbox[i]);

}

}


验证一下,在ajax请求之前,alert(this.content),在ajax请求之后,alert
(response.responseText),发现this.content就是超链接的url,response.responseText就是新
闻内容。联系到之前把新闻内容html文件改成utf8编码能正常显示,gb2312编码的新闻显示乱码,很明显是ajax从服务器收到的数据编码没有表
达清楚,google了一下发现,ajax默认使用utf8编码。现在问题清楚了,只要在显示新闻内容的时候告诉客户端这是一段gbk编码的内容就可以
了,或者在提交新闻的时候都使用UTF8编码来存储。前者修改代码量最少。这个时候自己又走进了另一个死胡同,自己一个劲儿地想怎么样让
prototypejs的ajax使用gbk编码,又用httpfox查看了每次ajax调用时的http response
header,发现其中的Content-Type的charset要么是utf8要么没有,所以又想着怎么样去修改http response
header中的content-type。又一头扎进去,google了很多prototypejs的资料。

其实这个时候离解决已经很接近了。自己知道ajax
request了一个html有点奇怪,也知道如果ajax请求的是一个CGI脚本就可以修改这个CGI脚本来设置http response
header了。这就是自己后来的解决方法:

// Begin Ajax request based off of the href of the clicked linked

loadInfo: function() {

var param='url='+this.content;

var myAjax = new Ajax.Request(

'test.php',

{method: 'get', parameters: param, onComplete:
this.processInfo.bindAsEventListener(this)}

);

},

然后在test.php中:

<?php

$mile_stone=1290736520;

$url = $_GET['url'];

#if(empty($url)) { echo "����ϵ����Ա"; }

$file_time = basename($url, '.html');

settype($file_time, "integer");

if($file_time > $mile_stone) header("Content-Type:
text/html;charset=gb2312");

else header("Content-Type: text/html;charset=utf8");

readfile($url);

?>


总结:

1) 时刻记住自己的目标,理智分析和取舍,不要依靠感觉和灵感来解决问题。

2)不要追求技术上的完美,尤其是不自量力的完美。

3)解决问题不止一种方法,先想再选最后执行。一个人不必也不可能样样通,选择自己最擅长的。

4)学习要靠书本交流和实践,有问题可以google一下作为参考。不要试图使用google去学习。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: