HttpServletRequest请求流只能读取一次的问题

问题当使用拦截器Interceptor的时候在中从request.getInputStream()后,controller将接收不到对应的参数,因为 请求流只能读取一次

下面介绍处理方式

我们可以利用HttpServletRequestWrapper来包装HttpServletRequest,将请求体中的流copy一份,覆写getInputStream()和getReader()方法供外部使用,同时自己定义一个getRequestBody()方法,用于直接获取Json参数。每次调用覆写后的getInputStream()方法都是从复制出来的二进制数组中进行获取,这个二进制数组在对象存在期间一直存在,这样就实现了流的重复读取。
 1、增强 requestwrapper

package com.multiplethread.multiplethread.selffilter;

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;

public class RequestWrapper extends HttpServletRequestWrapper {
    private final byte[] requestBody;


    /**
     * Constructs a request object wrapping the given request.
     *
     * @param request The request to wrap
     * @throws IllegalArgumentException if the request is null
     */
    public RequestWrapper(HttpServletRequest request)  throws IOException {
        super(request);
// 加上该部分获取一下【】, 才能支持  content-Type是 application/x-www-form-urlencoded 请求数据
        // 其实仅调用一一【request.getParameterMap()】该方法就可以将,请求参数放入request.getParam 相关对象,
        // 后续就可以获取
         request.getParameterMap()
        requestBody =  StreamUtils.copyToByteArray(request.getInputStream());
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(requestBody);
        return new ServletInputStream() {
            public boolean isFinished() {
                return false;
            }
            public boolean isReady() {
                return false;
            }
            public void setReadListener(ReadListener readListener) {}
            public int read() {
                return byteArrayInputStream.read();
            }
        };

    }

    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(this.getInputStream()));
    }


    public String getRequestBody() {
        return new String(this.requestBody);
    }

}

2、定义fitler

package com.multiplethread.multiplethread.selffilter;

import org.springframework.stereotype.Component;

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

@Component
@WebFilter
public class JsonFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
//请求参数为JSON类型,则使用自定义包装
if(request instanceof HttpServletRequest
&& "application/json".equals(((HttpServletRequest)request).getHeader("Content-Type"))) {
chain.doFilter(new RequestWrapper((HttpServletRequest) request), response);
// chain.doFilter(request, response); // 直接传有问题
} else {
chain.doFilter(request, response);
}

}

@Override
public void destroy() {

}
}
3、定义拦截器FirstInterceptor
package com.multiplethread.multiplethread.selffilter;

import com.alibaba.fastjson.JSONObject;
import org.springframework.lang.Nullable;
import org.springframework.util.StreamUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class FirstInterceptor implements HandlerInterceptor {
/**
* 控制器 请求执行前进入
* @param request
* @param response
* @param handler
* @return true:继续向下执行, false:不向下执行
*
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
String params = "";
if("application/json".equals(request.getHeader("Content-Type"))){
//通过方法直接获取JSON参数
ServletInputStream servletInputStream=request.getInputStream();
byte[] requestBody = StreamUtils.copyToByteArray(request.getInputStream());
System.out.println("requestBody.toString() = " + requestBody.toString());
System.out.println("new String(requestBody) = " + new String(requestBody));

}else {
params = JSONObject.toJSONString(request.getParameterMap());
}

if(!StringUtils.isEmpty(params)){
System.out.println("params = " + params);
}

return true;
}


/**
* 执行action
* @param request
* @param response
* @param handler
* @param modelAndView
* @throws Exception
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
@Nullable ModelAndView modelAndView) throws Exception {
}

}
4、注册拦截器
package com.multiplethread.multiplethread.selffilter;

import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.util.Arrays;
import java.util.List;

/**
* 用于注册 FirstInterceptor 拦截器
*/
@Component
public class MyWEbMvcConfigurer implements WebMvcConfigurer {
// 静态资源不进拦截器
static List<String> ListStaticResuource= Arrays.asList("/","/css/**","/img/**","/script/**","/pages/**");
@Override
public void addInterceptors(InterceptorRegistry registry){
registry.addInterceptor(getHandlerInterceptor()).excludePathPatterns(ListStaticResuource);
}


public HandlerInterceptor getHandlerInterceptor(){
return new FirstInterceptor();
}
}
5、定义controller 测试
package com.multiplethread.multiplethread.controller;

import com.multiplethread.multiplethread.Model.Questionnaire;
import com.multiplethread.multiplethread.Model.dataEnum.QuestionSwitchEnum;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

@RequestMapping("dianCaiBao")
@RestController
public class DianCaiBaoController {

@RequestMapping("getQuestionnaire")
public Questionnaire getQuestionnaire() {
Questionnaire questionnaire=new Questionnaire();
questionnaire.setQuestionSwitch(QuestionSwitchEnum.Open.getValue());
questionnaire.setQuestionUrl("https://www.baidu.com");
return questionnaire;
}
//{"questionSwitch":1,"questionUrl":"https://www.baidu.com"}


@RequestMapping(value = "saveQuestionnaire",method = {RequestMethod.POST,RequestMethod.GET})
public int getQuestionnaire(@RequestBody Questionnaire questionnaire) {
System.out.println("questionnaire = " + questionnaire);
// Questionnaire questionnaire=new Questionnaire();
// questionnaire.setQuestionSwitch(QuestionSwitchEnum.Open.getValue());
// questionnaire.setQuestionUrl("https://www.baidu.com");
return 1;
}
}

总结概述:RequestWrapper 实现将 request.getInputStream() 读取后放到requestBody 对象属性中
,后续操作都是从requestBody 对象属性中读取,这样就实现了可以多次读取的场景
参考:https://wenku.baidu.com/view/98adf15f8d9951e79b89680203d8ce2f01666559.html
posted @ 2022-06-09 07:10  xiaoBai1001  阅读(824)  评论(0编辑  收藏  举报