直奔主题
实现方法:
SpringMVC是通过前端控制器(DispatcherServlet)接收请求进行请求转发的,所以可以从这里入手把request对象替换为自定义的request对象,从而解决request的输入流只能读取一次的问题.
步骤:
1..重写service方法,实现替换request操作
package org.XXX.servlet; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;
// 自定义的Request对象 import org.XXX.request.RequestFacade; public class DispatcherServlet extends org.springframework.web.servlet.DispatcherServlet { /** * */ private static final long serialVersionUID = 6781076086037842254L; @Override protected void service(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException { // 全局替换request对象 super.service(requestConvert(request), response); } /** * 将apache的RequestFacade 转为自定义的 RequestFacade * @param request 自定义的RequestFacade * @return */ private HttpServletRequest requestConvert(HttpServletRequest request){ HttpServletRequest requestFacade = request; try { requestFacade = new RequestFacade((org.apache.catalina.connector.RequestFacade) request); } catch (Exception e) { logger.debug("RequestFacade对象转换失败"); } return requestFacade; } }
将我们自己的前端控制器配置到web.xml中
<servlet> <servlet-name>springmvcservlet-name> <servlet-class>org.XXX.servlet.DispatcherServletservlet-class> <init-param> <param-name>contextConfigLocationparam-name> <param-value>classpath:spring-servlet.xmlparam-value> init-param> <load-on-startup>1load-on-startup> servlet>
至此request替换操作已经完成,下面进入重点.
2.实现自己的RequestFacade
package org.XXX.request; import java.io.BufferedReader; import java.io.IOException; import javax.servlet.ServletInputStream; import org.apache.catalina.connector.CoyoteInputStream; import org.apache.catalina.connector.CoyoteReader; import org.apache.catalina.connector.InputBuffer; import org.apache.catalina.connector.Request; import org.apache.commons.io.IOUtils; import org.apache.tomcat.util.buf.ByteChunk; import org.apache.tomcat.util.buf.CharChunk; // 自己实现的简单的反射工具类 import org.XXX.util.ObjectUtil; public class RequestFacade extends org.apache.catalina.connector.RequestFacade { private byte[] postBody = null; /** * 重写构造器用于转换 * @param requestFacade * @throws IOException */ public RequestFacade(org.apache.catalina.connector.RequestFacade requestFacade) throws IOException { // 调用父构造器 super(ObjectUtil.get(requestFacade, "request", Request.class,true)); } /** * 获取 ServletInputStream
* 每次调用都会返回一个新的ServletInputStream * @return ServletInputStream */ @Override public ServletInputStream getInputStream() throws IOException{ // 获取新的CoyoteInputStream return CoyoteInputStream_.getCoyoteInputStream(getInputBuffer()); } /** * 获取 BufferedReader
* 每次调用都会返回一个新的BufferedReader * @return BufferedReader */ @Override public BufferedReader getReader() throws IOException{ return new CoyoteReader(getInputBuffer()); } /** * 获取新的InputBuffer * @return InputBuffer * @throws IOException */ private InputBuffer getInputBuffer() throws IOException{ byte[] postBody = getPostBody(); InputBuffer ib = new InputBuffer(postBody.length); // 初始化InputBuffer { ib.setRequest(getCoyoteRequest()); // 获取存储数据对象 ByteChunk bb = ObjectUtil.get(ib, "bb", ByteChunk.class,true); CharChunk cb = ObjectUtil.get(ib, "cb", CharChunk.class,true); // 写入postBody数据 bb.append(postBody, 0, postBody.length); cb.append(new String(postBody,request.getCharacterEncoding())); } return ib; } /** * 获取Request对象 * @return org.apache.coyote.Request * @throws IOException */ private org.apache.coyote.Request getCoyoteRequest() throws IOException{ ServletInputStream srcStream = request.getInputStream(); InputBuffer srcIb = ObjectUtil.get(srcStream, "ib", InputBuffer.class,true); return ObjectUtil.get(srcIb, "coyoteRequest", org.apache.coyote.Request.class,true); } /** * 获取PostBody * @return PostBody * @throws IOException */ public byte[] getPostBody() throws IOException{ if(postBody == null){ // 存储post内容,用于多次使用 postBody = IOUtils.toByteArray(request.getInputStream()); } return postBody; } /** * * 获取新的输入流 * @author GFuZan * */ private static class CoyoteInputStream_ extends CoyoteInputStream{ private CoyoteInputStream_(InputBuffer ib) { super(ib); } public static CoyoteInputStream getCoyoteInputStream(InputBuffer ib){ return new CoyoteInputStream_(ib); } } }
大功告成!!
最后附上AOP方法中读取post内容的部分代码
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); String requestBody = null; if ("POST".equals(request.getMethod())) { // 下面两种方式获取postBody都是可以的 //byte[] bytes = ((RequestFacade)request).getPostBody(); byte[] bytes = IOUtils.toByteArray(request.getInputStream()); requestBody = new String(bytes,request.getCharacterEncoding()); }else{ MapparameterMap = request.getParameterMap(); if(parameterMap != null && !parameterMap.isEmpty()){ requestBody = gson.toJson(parameterMap); } } if (requestBody != null && !requestBody.isEmpty()) { logger.info("requestBody: " + requestBody); }