解决Struts破坏Post数据一例

近日试用一个短信平台,支持接收用户回复的短信(上行短信)。发送,接收回执,查询余额等测试都没有问题。但是读取中文上行短信的时候总是出现乱码。

大致程序逻辑是这样的:我在短信平台中设置一个我自己的页面,如果有短信回复到我的专用号码时,短信平台会通过POST的方式把短信内容做GBK的URLENCODE编码后发给这个页面,我自己的程序可以做需要的处理(比如保存到数据库)。

接下来说一下曲折的解决过程吧,以便以后解决问题效率能有所提高:

直接用我的手机回复给我的短信平台专用号码,使用Struts2的action来接收(把这个Action设置为不需要登录),看到乱码,各种方式解码都无法得到正确的中文。

然后,尝试用PHP编写一个页面,把短信平台的上行接口改为这个页面,可以通过file_get_contents(“php://input”)得到未解码前的数据,自行解码后可以得到中文内容。

依此可证:对方的平台传给的数据是正确的。

接下来新建一个空的Java Web项目,只做一个jsp页面,接收数据转码后可以得到中文内容。

依此可证:普通的Java Web项目可以通过这个接口获得中文短信内容,问题出在我原来的这个项目上,很有可能是Struts的原因。

经过百谷查询后得知,Struts会自动对类型为application/x-www-form-urlencoded的POST数据进行解码,而且不会使用contentType里的chatset,现在的结论基本上明确了:Struts的拦截器自动把HttpRequest中的参数进行了错误的解码,接下来就是寻找解决方案了。

Struts是通过过滤器处理数据的,那可以在这个过滤器前再加个过滤器,只mapping上述特殊情况涉及到的link。处理原理大致是:request的参数数据只会被读取一次,那么加在Struts前的这个过滤器只要正确的读取了参数数据,再转交给Struts,Struts就不会再次处理了,程序就能读到正确的数据了。最早的尝试是:试图修改Request的ContentType以便逃过Struts的蹂躏,试过wrapper等方法均宣告失败,这个属性是只读的,无奈才有了下面的解决方案。这个过滤器写起来也很简单,在它的doFilter里有以下代码即可:

HttpServletRequest httpServletRequest = (HttpServletRequest)request;
httpServletRequest.setCharacterEncoding("GBK");
httpServletRequest.getParameterNames();
chain.doFilter(httpServletRequest, response);

 关键的一句话就是getParameterNames,这里不是真的想要读取数据,而是要让他先按我们的要求解码request内容,以防被Struts破坏。

把这个过滤器配置好之后再测试,已经能正常获取中文了。

测试过程中的另外一个小教训:每次测试都是用我的手机向短信平台发一个短信,然后等短信平台把这个短信内容上传到我的测试页面,每次测试成本是0.1元。对于没有明确方向,需要频繁测试的Case来说,确实是浪费了几块钱。一个更好的做法,再做一个测试类,用HttpPost对象直接发送测试数据到测试页面,这样不仅效率高,而且成本低,下次不要再犯这个错误了。

问题解决过程就是这些,太纠结了。