Tomcat是如何处理query/post的编码问题的?
首先我们看看get方式时,tomcat如何获取querystring。
比如我们的url是:http://abc.com/?keywords=%C6%A1%BE%C6 (其中%C6%A1%BE%C6是啤酒的GBK的URLEncode编码)
当请求来到服务器的tomcat(也可以说是Servlet)这一层面时,我们可以通过request获取这个keywords。获取的方式有两种:
(1) 直接使用getParameter("keywords")得到keywords参数
(2) 先使用getQueryString(),再手动解析出keywords参数
我们看看这两种方式。
(1) getParameter("keywords")
这里涉及 config/server.xml中URIEncode的配置项,假设我没对tomcat作任何URIEncoding的配置,它默认使用ISO-8859-1的编码。
当访问http://localhost/?keywords=%C6%A1%BE%C6后,我们试图打印出getParameter("keywords")的值,你猜看到什么?乱码!
我们试图质问tomcat:“为什么是乱码?!如果我输入的keywords是“啤酒”这样的字符,得到乱码我可以接受,但是我输入的可是“%C6%A1%BE%C6”这样一串ISO-8859-1编码的字符啊!
有人猜测,那是浏览器在搞鬼,浏览器发送的不是“%C6%A1%BE%C6”这样的可见字符的,而是发送“啤酒”这个非ascii字符(当然,我们应该理解本质上传送的“啤酒”肯定是“啤酒”的某某编码格式的字节码,这个编码可以是常见的GBK或UTF-8)。
但是,其实不是浏览器搞鬼,浏览器发送的就是“%C6%A1%BE%C6”,你可以通过打印request.getQueryString();看到,它输出“keywords=%C6%A1%BE%C6”。
那到底是怎么回事呢?
源于tomcat对表单提交的参数(包括Get方式和Post方式)有个UDecode类的处理过程。什么是UDecode类的处理过程,详细你可以看tomcat源码的UDecode类,这里用简单的一句话概括UDecode类做的事情:修改request中的parameter(包括get方式的url参数的value和post方式的application/x-www-form-urlencoded的参数的value)的字节, 将每1个“%HH”(占3个字节)变成“0xHH”(占1个字节),且将加号"+"(占1个字节)变成空格“ ”(占1个字节)。下面的图直观的描述了这个UDecode过程:
我们看到6个字节的“%D6%D0”变成了2个字节的“[0xD6][0xD0]”,我用中括号“[ ]”来表示其中的内容合起来是一个字节。
到这里,我们至少知道了tomcat有个UDecode的过程,所以我们从中推理出这样的一个现象:客户端发送的keywords参数无论是啤酒的GBK字节码“[0xC6][0xA1][0xBE][0xC6]”还是啤酒的GBKURLEncode字符“%C6%A1%BE%C6”,经过tomcat的UDecode后,到变成前者,即“[0xC6][0xA1][0xBE][0xC6]”。
说到这,我再次问上面一开始的那个问题:getParameter("keywords")的值为什么是乱码?我给个提示:和UDecode有关!答案现在还不是很明显,但我们脑海可能浮现了这幅图:从UDecode的结果“[0xC6][0xA1][0xBE][0xC6]”到getParameter("keywords")的乱码。
你想到问号的部分是什么吗?很抽象地为你解开答案:
所以,getParameter("keywords")的值为什么是乱码?因为上面的bytes是GBK的字节码,而tomcat错误地将bytes作为ISO-8859-1进行解码了!难怪keywords会是乱码!
这...是tomcat的错吗?坦诚说不是,因为上面“String keywords = new String(bytes,"ISO-8859-1");”中的编码"ISO-8859-1"是可配置的,大家还记得 config/server.xml中的URIEncode配置项吧?就是这个URIEncode配置项,决定是上面的式子用什么编码。
到了这里,大家都清楚了,设置配置项URIEncode=“GBK”就可以用getParameter("keywords")得到正确的keywords:“啤酒”。
另外配置项useBodyEncodingForURI使得
http://wiki.apache.org/tomcat/FAQ/CharacterEncoding#Q2
http://confluence.atlassian.com/display/DOC/Configuring+Tomcat%27s+URI+encoding
-------------------------------------------------------------------------------------------------------------------------
现在,我问大家一个问题,当你知道,浏览器无论访问“http://localhost/?keywords=%C6%A1%BE%C6” 还是 “http://localhost/?keywords=[0xC6][0xA1][0xBE][0xC6]”,tomcat都会将keywords变成“[0xC6][0xA1][0xBE][0xC6]”后,大家觉得要怎么去解码还原keywords为“啤酒”呢?
相信答案大家都很一致:
byte[] bit = new byte[]{(byte)0xD6,(byte)0xD0}; "中"的字节码,是URLDecode之后的字节码
String w = new String(bit, "GBK"); 用配置的URIEncoding将其解码,变成字符"中"
String ISOkeywords = request.getParameter("keywords");
String keywords = null;
request.getQueryString();//keywords=%C6%A1%BE%C6
if(StringUtil.isNotBlank(ISOkeywords)){
try {
keywords = new String(ISOkeywords.getBytes("ISO-8859-1"), "GBK");
Tomcat处理编码的流程
最新推荐文章于 2021-07-11 15:38:03 发布