SpringMVC无法获取请求中的参数的问题的调查与解决
Posted 路漫漫其修远兮
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringMVC无法获取请求中的参数的问题的调查与解决相关的知识,希望对你有一定的参考价值。
由于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无法获取请求中的参数的问题的调查与解决的主要内容,如果未能解决你的问题,请参考以下文章
SpringMVC— “SpringMVC获取请求参数的方法”
SpringMVC— “SpringMVC获取请求参数的方法”
SpringMVC— “SpringMVC获取请求参数的方法”
SpringMVC— “SpringMVC获取请求参数的方法”
Spring MVC 学习笔记 --- [SpringMVC的几个注解标签说明,获取请求数据,springmvc提供的中文乱码过滤配置]