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

本文介绍了一种在SpringMVC中解决HTTP请求输入流只能读取一次的问题的方法。通过自定义DispatcherServlet和RequestFacade,实现了请求输入流的多次读取。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

直奔主题

  实现方法:

    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.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<br/>
     * 每次调用都会返回一个新的ServletInputStream
     * @return ServletInputStream
     */
    @Override
    public ServletInputStream getInputStream() throws IOException{
        // 获取新的CoyoteInputStream
        return CoyoteInputStream_.getCoyoteInputStream(getInputBuffer());
    }
    
    /**
     * 获取 BufferedReader<br/>
     * 每次调用都会返回一个新的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{
                Map<String,String[]> parameterMap = request.getParameterMap();
                if(parameterMap != null && !parameterMap.isEmpty()){
                    requestBody = gson.toJson(parameterMap);
                }
            }
            if (requestBody != null && !requestBody.isEmpty()) {
                logger.info("requestBody: " + requestBody);
            }

 

转载于:https://www.cnblogs.com/gfuzan/p/10583024.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值