SpringMVC无法获取请求中的参数的问题的调查与解决(2)
由于Request的getInputSteam()一旦获取一次后,就再也无法获取了
在实际项目中导致下面的问题:
1,多个拦截器,Filter都需要从InputStream中拿数据的情况无法处理;
2,InputStream被拦截器,Filter拿走了,Controller层无法getParameter(),也无法使用@RequestParam 注解来自动匹配设置参数。
参考了多篇处理getInputStream()相关的博文后,采取了下面的解决方案。
1,通过继承HttpServletRequestWrapper创建自定义的Request对象,
在这个对象中,以字节数组形式长期保持Request的Stream内容,可以在拦截器,Filter,Controller中任意多次复用;
同时,重写getParameter()相关的各个方法,确保从保持的Stream数据中,可以正常解析所有参数,并且在解析过程中,可以进行自定义的解码操作。
package org.jiagoushi.mobile.rule.filter; import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.StringTokenizer; import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper; import com.mtime.mobile.utils.Encodes; public class MAPIHttpServletRequestWrapper extends HttpServletRequestWrapper {
private Map<String, String[]> paramsMap; @Override
public Map getParameterMap() {
return paramsMap;
} @Override
public String getParameter(String name) {// 重写getParameter,代表参数从当前类中的map获取
String[] values = paramsMap.get(name);
if (values == null || values.length == 0) {
return null;
}
return values[0];
} @Override
public String[] getParameterValues(String name) {// 同上
return paramsMap.get(name);
} @Override
public Enumeration getParameterNames() {
return Collections.enumeration(paramsMap.keySet());
} private String getRequestBody(InputStream stream) {
String line = "";
StringBuilder body = new StringBuilder();
int counter = 0; // 读取POST提交的数据内容
BufferedReader reader = new BufferedReader(new InputStreamReader(stream));
try {
while ((line = reader.readLine()) != null) {
if (counter > 0) {
body.append("\r\n");
}
body.append(line);
counter++;
}
} catch (IOException e) {
e.printStackTrace();
} return body.toString();
} private HashMap<String, String[]> getParamMapFromPost(HttpServletRequest request) { String body = "";
try {
body = getRequestBody(request.getInputStream());
} catch (IOException e) {
e.printStackTrace();
}
HashMap<String, String[]> result = new HashMap<String, String[]>(); if (null == body || 0 == body.length()) {
return result;
} return parseQueryString(body);
} // 自定义解码函数
private String decodeValue(String value) {
if (value.contains("%u")) {
return Encodes.decodeUnicode(value);
} else {
try {
return URLDecoder.decode(value, "UTF-8");
} catch (UnsupportedEncodingException e) {
return "";// 非UTF-8编码
}
}
} public HashMap<String, String[]> parseQueryString(String s) {
String valArray[] = null;
if (s == null) {
throw new IllegalArgumentException();
}
HashMap<String, String[]> ht = new HashMap<String, String[]>();
StringTokenizer st = new StringTokenizer(s, "&");
while (st.hasMoreTokens()) {
String pair = (String) st.nextToken();
int pos = pair.indexOf('=');
if (pos == -1) {
continue;
}
String key = pair.substring(0, pos);
String val = pair.substring(pos + 1, pair.length());
if (ht.containsKey(key)) {
String oldVals[] = (String[]) ht.get(key);
valArray = new String[oldVals.length + 1];
for (int i = 0; i < oldVals.length; i++) {
valArray[i] = oldVals[i];
}
valArray[oldVals.length] = decodeValue(val);
} else {
valArray = new String[1];
valArray[0] = decodeValue(val);
}
ht.put(key, valArray);
}
return ht;
} private Map<String, String[]> getParamMapFromGet(HttpServletRequest request) {
return parseQueryString(request.getQueryString());
} private final byte[] body; // 报文 public MAPIHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
super(request);
body = readBytes(request.getInputStream()); // 首先从POST中获取数据
if ("POST".equals(request.getMethod().toUpperCase())) {
paramsMap = getParamMapFromPost(this);
} else {
paramsMap = getParamMapFromGet(this);
} } @Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(getInputStream()));
} @Override
public ServletInputStream getInputStream() throws IOException {
final ByteArrayInputStream bais = new ByteArrayInputStream(body);
return new ServletInputStream() { @Override
public int read() throws IOException {
return bais.read();
}
};
} private static byte[] readBytes(InputStream in) throws IOException {
BufferedInputStream bufin = new BufferedInputStream(in);
int buffSize = 1024;
ByteArrayOutputStream out = new ByteArrayOutputStream(buffSize); byte[] temp = new byte[buffSize];
int size = 0;
while ((size = bufin.read(temp)) != -1) {
out.write(temp, 0, size);
}
bufin.close(); byte[] content = out.toByteArray();
return content;
}
}
2,自定义一个Filter(这个Filter的配置位置放在最靠前的位置),替换掉原始的Request对象:
web.xml中最靠前的位置增加Filter
... <context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/api-spring.xml</param-value>
</context-param> <listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener> <!-- 下面的Filter以自定义的Request对象替换掉原有的Request,进行POST数据的保持和自定义解码 -->
<!-- 可以解决下面两个问题:
1,CheckUrl拦截器读取InputStream导致getParameter()方法失效;
2,POST数据由于有自定义Unicode编码,解码时也需要读取InputSteam
-->
<filter>
<filter-name>requestFilter</filter-name>
<filter-class>org.jiagoushi.mobile.rule.filter.HttpServletRequestReplacedFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>requestFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping> ...
Filter类的代码
package org.jiagoushi.mobile.rule.filter; import java.io.IOException; import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest; public class HttpServletRequestReplacedFilter implements Filter {
@Override
public void destroy() { } @Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException { ServletRequest requestWrapper = null;
if(request instanceof HttpServletRequest) {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
if("POST".equals(httpServletRequest.getMethod().toUpperCase())){
requestWrapper = new MAPIHttpServletRequestWrapper((HttpServletRequest) request);
}
} if(requestWrapper == null) {
chain.doFilter(request, response);
} else {
chain.doFilter(requestWrapper, response); //替换!
}
} @Override
public void init(FilterConfig arg0) throws ServletException { } }
---
参考资料:
http://ayaoxinchao.iteye.com/blog/2110902
http://my.oschina.net/tmallMoney/blog/127225
http://www.xuebuyuan.com/809435.html
http://hw1287789687.iteye.com/blog/1923085
SpringMVC无法获取请求中的参数的问题的调查与解决(2)的更多相关文章
- SpringMVC无法获取请求中的参数的问题的调查与解决(1)
*更新:本文第一版中犯了比较大的错误,无论@RequestBody还是@RequestParam注解一样,都会使用全局的Encoding进行解码,会导致特殊编码的参数值丢失. 只要抛弃掉注解,就完全可 ...
- 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 ...
随机推荐
- List的Capacity
Capacity 在.NET中List的容量应该只是受到硬件限制. 属性Capacity的真正含义,是创建List时给它预分配的容量. 一旦项的数量超过了当前的Capacity,Capacity会以 ...
- IOS开发-图片上传
目前IOS端开发,图片上传到服务器分为两种,一种是直接上到服务器,一种是借助第三方储存(减少服务器压力). 一.直接上传到服务器 /** * 代码演示 */ //*******UIImagePNGRe ...
- Hibernate反向工程生成DAO
通过Hibernate反向工程生成个DAO: package dao; import java.util.List; import org.hibernate.LockMode; import org ...
- Win7系统安装好Axure点击运行报.NET Framework4.0未安装的解决办法
1:问题 由于工作需要,需要研究一下Axure原型设计软件的使用方式,在公司的电脑上成功安装了从同事那里拿来的Axure7.0软件,能够正确运行没有任何问题,在自己的电脑上安装的也非常顺利,不过运 ...
- IOC框架整体介绍
1.Castle Windsor 2.Autofac 3.Unity 4.Spring.NET 5.StructureMap 6.Ninject
- mysql grant命令错误:ERROR 1044 (42000): Access denied for 'root' With All Privileges
http://stackoverflow.com/questions/21714869/error-1044-42000-access-denied-for-root-with-all-privile ...
- 解析PHP正则提取或替换img标记属性
<?php/*PHP正则提取图片img标记中的任意属性*/$str = '<center><img src="/uploads/images/20100516000. ...
- java和h5 canvas德州扑克开发中(一)
先附上我的德州扑克测试地址 http://120.26.217.116:8080/LxrTexas/texasIndex.html 我和一个朋友的德州扑克历时一个多月开发,目前已经基本可玩. 前端主要 ...
- VS2013,VS2015设置类模板文件表头
一般VS的类模板文件是放在C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\IDE\ItemTemplatesCache\CSha ...
- CentOS开机无法进入系统,如何查错
开机时按e/F5按钮,进入选择系统界面 会出现 CentOS(2.6.32-...类似的选择列表,选择默认的系统然后按e: 这时会出现 root kernel ... initd... 三个选项,选择 ...