SpringMVC无法获取请求中的参数的问题的调查与解决(1)
*无论@RequestBody还是@RequestParam注解一样,都会使用全局的Encoding进行解码,会导致特殊编码的参数值丢失。
只要抛弃掉注解,就完全可以在Controller层得到请求的Raw数据!
-----
使用框架可以节约开发时间,但有时由于隐藏了一些实现细节,导致对底层的原理知之不详,碰到问题时不知道该从哪一个层面入手解决。因此我特意记录了下面这个典型问题的调查和解决过程供参考。
事情是这样的,我们原来有一个移动端调用的发表评论的API,是几年前在NET平台上开发的,移植到JAVA后,发现安卓版APP无法正常发表汉字评论。
基于SpringMVC创建的JAVA版API接口大致如下,经调查发现,关键的content参数,在Controller层检查结果为空。
@RequestMapping(value = "/Test.api")
public Object test(
HttpServletRequest request,
HttpServletResponse response,
@RequestParam(value = "content", required = false, defaultValue="") String content) { // 在这里,content的值为空 }
用Charles抓包检查Post的Form数据,确实有字段content,且有汉字值。但检查其Raw数据居然为这样的形式:content=%u611f%u53d7%u4e00%u4e0b%u8d85%u4eba%u7684%u808c%u8089%uff0c
我们知道,目前java常用的URLEncoder类,一般将汉字转换成"%xy"的形式,xy是两位16进制的数值,不会出现%u后面跟4个字符这种情况。
%u开头代表这是一种Unicode编码格式,后面的四个字符是二字节unicode的四位16进制码。在Charles软件上,支持这种解码,所以可以正常看到抓包数据中的汉字。
但是我们从SpringMVC框架层面统一指定了encoding为UTF-8,根据@RequestParam注解,使用UTF-8进行content参数的解码时,必然异常,由此导致了Post过来的content字段丢失。
和安卓团队确认,发现过去他们确实采用了自己独有的Encode方法对Post数据进行编码:
public static String UrlEncodeUnicode(final String s)
{
if (s == null)
{
return null;
}
final int length = s.length();
final StringBuilder builder = new StringBuilder(length); // buffer
for (int i = 0; i < length; i++)
{
final char ch = s.charAt(i);
if ((ch & 0xff80) == 0)
{
if (Utils.IsSafe(ch))
{
builder.append(ch);
}
else if (ch == ' ')
{
builder.append('+');
}
else
{
builder.append("%");
builder.append(Utils.IntToHex((ch >> 4) & 15));
builder.append(Utils.IntToHex(ch & 15));
}
}
else
{
builder.append("%u");
builder.append(Utils.IntToHex((ch >> 12) & 15));
builder.append(Utils.IntToHex((ch >> 8) & 15));
builder.append(Utils.IntToHex((ch >> 4) & 15));
builder.append(Utils.IntToHex(ch & 15));
}
}
return builder.toString();
}
采用这种方式的原因已经不可考证,并且安卓团队已经决定将在未来版本中放弃该编码方式,采用JAVA常用的Encoder类进行UTF-8的编码。问题定位后,决定新版API中必须要兼容新旧两种编码方式。
但是目前SpringMVC的@RequestParam注解负责了请求数据的解码,我们从哪一层切入,截获请求数据,判断其编码方式,并动态选用不同的解码方式来处理呢?
经过DEBUG,觉得下面的方式是可行的。
解决问题的步骤:
修改API的接口形式,放弃@RequestParam注解,放弃@RequestBody注解,在Controller层直接从request的stream中得到参数的raw数据并自己解析
@RequestMapping(value = "/Test.api")
public Object test(
HttpServletRequest request,
HttpServletResponse response) { String line = "";
StringBuilder body = new StringBuilder();
int counter = 0; InputStream stream;
stream = request.getInputStream(); //读取POST提交的数据内容
BufferedReader reader = new BufferedReader(new InputStreamReader(stream));
while ((line = reader.readLine()) != null) {
if(counter > 0){
body.append("\r\n");
}
body.append(line);
counter++;
} //POST请求的raw数据可以获得
System.out.println(body.toString()); // 使用自定义的解析工具类对body的内容进行解码
RequestParamMap map = new RequestParamMap(body.toString());
...
}
只要在进入controller层之前,确保没有别的Filter之类调用了request.getInputStream()就没有问题,因为request.getInputStream()只能被碰一次,之后就再无法获取原始的请求数据了。
然而,接着就发现,确实有一个自定义的拦截器,需要获取POST的InputStream数据...
http://www.cnblogs.com/csliwei/p/5557353.html
-----
SpringMVC无法获取请求中的参数的问题的调查与解决(1)的更多相关文章
- SpringMVC无法获取请求中的参数的问题的调查与解决(2)
由于Request的getInputSteam()一旦获取一次后,就再也无法获取了 在实际项目中导致下面的问题: 1,多个拦截器,Filter都需要从InputStream中拿数据的情况无法处理: 2 ...
- Spring MVC如何获取请求中的参数
目录 一.获取URL中路径参数 1.1 @PathVariable 注解 1.2 @PathParam 注解 二.获取请求参数: 2.1 GET请求 2.1.1 获取请求中的单个参数:@Request ...
- 学习SpringMVC——如何获取请求参数
@RequestParam,你一定见过:@PathVariable,你肯定也知道:@QueryParam,你怎么会不晓得?!还有你熟悉的他(@CookieValue)!她(@ModelAndView) ...
- SpringMVC参数绑定(从请求中接受参数)
参数绑定(从请求中接收参数) 1)默认类型: 在controller方法中可以有也可以没有,看自己需求随意添加. httpservletRqeust,httpServletResponse,httpS ...
- springboot的restful风格获取请求中携带的参数
http://localhost:8080/emp/1 有以上请求,我们controller要怎么获取请求中传递的参数1呢? 通过PathVariable注解,如下: @DeleteMapping(& ...
- 获取url中的参数\+发送ajax请求根路径|+获取复选框的值
//获取url中的参数function getUrlParam(name) { var reg = new RegExp("(^|&)" + name + "=( ...
- CSS样式表、JS脚本加载顺序与SpringMVC在URL路径中传参数与SpringMVC 拦截器
CSS样式表和JS脚本加载顺序 Css样式表文件要在<head>中先加载,这样网页显示时可以第一次就渲染出正确的布局和样式,网页就不会闪烁,或跳变 JS脚本尽可能放在<body> ...
- APPCAN开发笔记:html页面之间的参数传递:使用js获取url中的参数,以及在APPCAN中不能使用的解决方法
用PHP的GET/POST方式来传递方式已经是司空见惯了,但是如果我的页面是一个静态的html的页面,想传递参数的时候要怎么办呢?在APPCAN的开发中我们会经常遇到这样的问题,因为所有的页面都是静态 ...
- 用JS获取地址栏中的参数的简易方法
这个方法用起来超级简单,传入参数即可直接获取地址栏中的参数 代码如下 function GetQueryString(name) { var reg = new RegExp("(^|&am ...
随机推荐
- Install Houdini 12.5 x64 in CentOS 7
Thanks for Must(QQ ID)'s big help for installing Houdini in CentOS7. - download HOUDINI_FX_V12.5.371 ...
- cookie禁用了,session还能用吗?
Cookie与 Session,一般认为是两个独立的东西,Session采用的是在服务器端保持状态的方案,而Cookie采用的是在客户端保持状态的方案.但为什么禁用Cookie就不能得到Session ...
- Scala的几个小tips
1. Main方法只能写在object而不是class里 2. Unit test只能针对class或者trait,不能给object做,解决方法,把object里面要测的方法拿出来放到trait里, ...
- [HTML] IE=edge,chrome=1的META标签详解
文件兼容性用于定义让IE如何编译你的网页.此文件解释文件兼容性,如何指定你网站的文件兼容性模式以及如何判断一个网页该使用的文件模式. meta信息中常有这么一句: <meta content=& ...
- firefox火狐浏览器过滤广告插件:Adblock Plus
firefox火狐浏览器过滤广告插件:Adblock Plus
- stickUp让页面元素“固定”位置
stickUp能让页面目标元素“固定”在浏览器窗口的顶部,即便页面在滚动,目标元素仍然能出现在设定的位置. http://www.bootcss.com/p/stickup/
- LinkedList详细分析
一.源码解析1. LinkedList类定义2.LinkedList数据结构原理3.私有属性4.构造方法5.元素添加add()及原理6.删除数据remove()7.数据获取get()8.数据复制clo ...
- Rhino+envjs-1.2.js 在java运行网站js 工具类
java爬虫遇到个页面加密的东西,找了些资料学习学习 做了个java运行js的工具类,希望对大家有用,其中用到client(获取js)可以自行换成自己的client.主要是用了 Rhino就是Java ...
- 【原创】Linux常用管理命令总结
一.文件夹操作:1.查看文件夹ls [-al]/dir Diredtory_Name2.建立文件夹mkdir [-p] Diredtory_Name3.删除文件夹rm -r[f] Diredtory_ ...
- XE7 & IOS开发之开发账号(2):发布证书、发布授权profile的申请使用,附Ad hoc真机调试、生成ipa文件演示(XCode所有版本通用,有图有真相)
网上能找到的关于Delphi XE系列的移动开发的相关文章甚少,本文尽量以详细的图文内容.傻瓜式的表达来告诉你想要的答案. 原创作品,请尊重作者劳动成果,转载请注明出处!!! 注意,以下讨论都是以&q ...