β

Tornado 开发过程中遇到的参数中文值编码问题

Dndx's Blog 3139 阅读

前天在开发魔兽世界服务器状态查询API的时候,被一个URL编码的问题困扰了很久,今天抽空写下来,一是当个笔记,二是希望其它遇到类似问题的朋友能有个参考。

废话不多说,先来看一个简单的Tornado程序:

#coding:utf-8

import tornado.ioloop
import tornado.web

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.write(self.get_argument('param', 'No input.'))

application = tornado.web.Application([
    (r"/", MainHandler),
])

if __name__ == "__main__":
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()

这是一个白痴都能看懂的程序,似乎很没意思:

Running Screenshot

好了,现在让我们试一试“http://127.0.0.7:8888/?param=你好”,当你敲下回车时,FireFox自动将地址转换成了“http://127.0.0.7:8888/?param=%C4%E3%BA%C3”,一切都是那么的顺理成章,但是结果可能会让你失望:

Running Screenshot

啊?怎么会500,我们再换成IE试一下:

Running Screenshot

结果更惨呢,请注意,IE为我们自动选择了编码“UTF-8”,回忆到get_argument的文档里面有这么一句:

“The returned value is always unicode.”

考虑到Python的Unicode对象在内部是UTF-16的实现,怀疑是UTF-16被识别成了UTF-8导致乱码,于是修改代码,将上面的

self.write(self.get_argument('param', 'No input.'))

改为

self.write(self.get_argument('param', 'No input.').encode('utf-8'))

结果很残酷,IE仍然乱码,乱码内容没有任何改变。

问题至此陷入僵局,IE一直乱码不说,FF竟然直接500,于是赶紧上python-cn提问

看到“闲云无心”提到可能的编码问题,联系到500报错提示 UnicodeDecodeError: 'utf8' codec can't decode byte 0xc4 in position 0: invalid continuation byte ,于是有了如下的分析过程:

0xc4对应的就是param中的%C4,Tornado对参数有着一套系统的处理流程,其中很重要的一步就是将字符串解码成Unicode,现在既然以utf-8格式解码失败,难不成“%C4%E3%BA%C3”不是UTF-8?

马上访问中国站长站的在线urlencode工具,印证了我的猜想,“你好”这两个字的UTF-8格式urlencode结果应该为“%e4%bd%a0%e5%a5%bd”,将URL替换为“http://127.0.0.7:8888/?param=%e4%bd%a0%e5%a5%bd”,IE、FF打开均能正常显示“你好”两字。

至此,困扰了我2天的问题成功解决。虽然问题说到底很简单,但是会让人很困惑。。

另:python-cn讨论贴中的Inside给出了对此现象的解释:

看来你是不知道在浏览器地址栏手动输入中文和在页面上一个的链接的编码处理方式是不同的。。。。

打个比方,在windows系统上,你在FF地址栏输入"http://localhost/中文.html?m=汉语",这里的“中文”两字的编码是utf8(这一点应该是跟浏览器设置相关),而“汉语”则是gbk,跟操作系统相关(大部分中国人的windows应该都是cp936,也就是gbk)。

如果你是通过某个页面访问这个链接的,则所有字符的编码都是跟页面的编码相关。

在IE上也是一样。

所以,我觉得还是打消在浏览器地址栏输入中文这个想法吧,要不然你要解码两次,而且还要保证页面上的编码跟系统一样,不然无法保证手动输入和页面点击的兼容性。。。。
作者:Dndx's Blog
原文地址:Tornado 开发过程中遇到的参数中文值编码问题, 感谢原作者分享。

发表评论