Spring MVC 解决request.getInputStream只能读取一次问题(全局替换request)

直奔主题

  实现方法:

    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>springmvc</servlet-name>
        <!-- <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> -->
        <servlet-class>org.XXX.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:spring-servlet.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

      至此request替换操作已经完成,下面进入重点.

  2.实现自己的RequestFacade

package org.XXX.request;

import java.io.IOException;
import javax.servlet.ServletInputStream;
import org.apache.catalina.connector.CoyoteInputStream;
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 final byte[] postBody; /** * 重写构造器用于转换 * @param requestFacade * @throws IOException */ public RequestFacade(org.apache.catalina.connector.RequestFacade requestFacade) throws IOException { // 调用父构造器 super(ObjectUtil.get(requestFacade, "request", Request.class,true)); // 存储post内容,用于多次使用 postBody = IOUtils.toByteArray(request.getInputStream()); } /** * 获取 ServletInputStream<br/> * 每次调用都会返回一个新的ServletInputStream,解决只能读取一次问题 * @return ServletInputStream */ @Override public ServletInputStream getInputStream() throws IOException{ // 获取新的CoyoteInputStream return CoyoteInputStream_.getCoyoteInputStream(getInputBuffer()); } /** * 获取新的InputBuffer * @return InputBuffer * @throws IOException */ private InputBuffer getInputBuffer() throws IOException{ InputBuffer ib = new InputBuffer(); // 初始化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内容
/** * 获取PostBody * @return PostBody */ public byte[] getPostBody(){ 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{
                Map<String,String[]> parameterMap = request.getParameterMap();
                if(parameterMap != null && !parameterMap.isEmpty()){
                    requestBody = gson.toJson(parameterMap);
                }
            }
            if (requestBody != null && !requestBody.isEmpty()) {
                logger.info("requestBody: " + requestBody);
            }

猜你喜欢

转载自www.cnblogs.com/gfuzan/p/10583024.html