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
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构