解决HttpServletRequest InputStream(错误提示Request body missin )只能使用一次的问题

通常我只定义一个拦截器,对请求参数处理的时候,尤其是对post需要用到流,那这样处理完后跑到控制器那边就会报request body missing

错误提示:
流已经关闭,因为如果通过过滤拦截器读取流的话,IO流关闭只能读取一次, 即使不关闭的话,流有个read标志位,后续控制器会从read标志位开始读,读过流之后就读取不到数据了,除非利用void reset()方法,把pos位置位开始,重新读,但是不是任何流都可以使用,所以我们写通用的方法时候,读取完流中数据之后,需要进行包装request,将流重新写入,供后续控制器读取。

网络上解释如下:
那是因为流对应的是数据,数据放在内存中,有的是部分放在内存中。read 一次标记一次当前位置(mark position),第二次read就从标记位置继续读(从内存中copy)数据。 所以这就是为什么读了一次第二次是空了。 怎么让它不为空呢?只要inputstream 中的pos 变成0就可以重写读取当前内存中的数据。javaAPI中有一个方法public void reset() 这个方法就是可以重置pos为起始位置,但是不是所有的IO读取流都可以调用该方法!ServletInputStream是不能调用reset方法,这就导致了只能调用一次getInputStream()。

解决方法

1.自定义一个request

package com.zhongyun.zycx.utils;

import org.springframework.util.StreamUtils;

import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.Charset;

/**
 * @author lc
 * @version 1.0
 * @date 2021/10/27 10:27
 */
public class MyHttpServletRequestWrapper extends HttpServletRequestWrapper {

    private final byte[] body;

    private String bodyString;

    public MyHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        this.bodyString = StreamUtils.copyToString(request.getInputStream(), Charset.forName("UTF-8"));
        body = bodyString.getBytes("UTF-8");
    }

    @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 boolean isFinished() {
                return false;
            }

            @Override
            public boolean isReady() {
                return false;
            }

            @Override
            public void setReadListener(ReadListener readListener) {

            }

            @Override
            public int read() throws IOException {
                return bais.read();
            }
        };
    }

}

写一个过滤器

一定要在启动类上加@ServletComponentScan(“com.zhongyun.zycx.handler”)

package com.zhongyun.zycx.handler;

import com.zhongyun.zycx.utils.MyHttpServletRequestWrapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

@WebFilter(urlPatterns = "/*",filterName = "channelFilter")
public class ChannelFilter implements Filter {
    private Logger logger = LoggerFactory.getLogger(ChannelFilter.class);

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        logger.info("================进入过滤器======================");
        // 防止流读取一次后就没有了, 所以需要将流继续写出去

        filterChain.doFilter(new MyHttpServletRequestWrapper((HttpServletRequest) servletRequest), servletResponse);


    }

    @Override
    public void destroy() {

    }
}

拦截器

package com.zhongyun.zycx.handler;

import net.sf.json.JSONObject;
import org.springframework.web.servlet.HandlerInterceptor;
import weixin.popular.util.StreamUtils;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.nio.charset.Charset;

/**
 * @author lc
 * @version 1.0
 * @date 2021/10/26 14:51
 */
public class TicketsInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        crossDomain(request,response); // 设置请求头,解决跨域问题
        String word = "测试";
        String method = request.getMethod();if ("GET".equals(method)) {
            if (request.getParameter("word") != null) {
                word = request.getParameter("word");
            }
        }
        if ("POST".equals(method)) {
            String paramstr = StreamUtils.copyToString(request.getInputStream(), Charset.defaultCharset());
            JSONObject jsonObject = JSONObject.fromObject(paramstr);
            if (jsonObject.containsKey("word")) {
                word = jsonObject.getString("word");
            }
        }
        if (word == null || word.length() < 2) {
            response.setStatus(500, "word最少为2位");
            return false;
        }
        System.out.println("释放拦截器");
        return true;
    }

	// 跨域
    public void crossDomain(HttpServletRequest request, HttpServletResponse response) {
        response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));
        response.setHeader("Access-Control-Allow-Credentials", "true");
    }
}


拦截器配置

package com.zhongyun.zycx.handler;

import org.aopalliance.intercept.Interceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.InterceptorRegistration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * 拦截器的配置
 *
 * @author lc
 * @version 1.0
 * @date 2021/10/26 14:58
 */
@Configuration//标识这是一个配置类
public class InterceptorConfiguration implements WebMvcConfigurer {

    // 拦截路径
    @Override
    public void addInterceptors(InterceptorRegistry registry) {

        //注册Interceptor拦截器(Interceptor这个类是我们自己写的拦截器类)
        InterceptorRegistration registration = registry.addInterceptor(new TicketsInterceptor());
        //addPathPatterns()方法添加需要拦截的路径
        registration.addPathPatterns("/openapi/**");      //路径都被拦截
        //excludePathPatterns()方法添加不拦截的路径
        registration.excludePathPatterns(                         //添加不拦截路径
                "/demo/loginPage",            //登录页面的地址【不拦截】
                "/**/*.html",            //html静态资源
                "/**/*.js",              //js静态资源
                "/**/*.css"              //css静态资源
        );
    }

    // 跨域,上面的拦截器配置了
    /*@Override
    public void addCorsMappings(CorsRegistry registry) {

    }*/
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

LC超人在良家

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值