问题背景
HttpServletRequest.getReader()
HttpServletRequest.getInputStream() 不能在过滤器中读取一次二进制流(字符流),又在另外一个Servlet中读取一次,即一个InputSteam(BufferedReader)对象在被读取完成后,将无法再次被读取。二进制流被读取后,字节流/字符流的下标将发生变化,假如程序中重新调用一遍getReader/getInputStream 将会提示异常
解决方案
思路很简单,既然HttpServletRequest的请求体无法通过getReader/getInputStream再次调用,那我们只需要包装一层,通过Wrapper对象去集成HttpServletRequest的所有能力,并将请求体抽离出来,这样每次读取我们定义的请求体,问题就解决了。这个方法非常灵活。
画个流程图给大家解释下
通过上述流程图,可以得到关键的两个信息
1、定义包装类HttpServletRequestWrapper
2、全局过滤器filter,把HttpServletRequest包装成
ps:幸好的是,java语言设计者也考虑到了这种场景,已经帮我们准备好Wrapper类,直接集成使用即可。
代码示例
包装类定义(RepeatedlyReadRequestWrapper.java)
package com.whale.finance.filter;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;
/**
* @author yanyq
* @desc 重复读取HttpServletRequest reader/inputstream
* @date 2023/7/27
*/
public class RepeatedlyReadRequestWrapper extends HttpServletRequestWrapper {
private final String body;
/**
*
* @param request
*/
public RepeatedlyReadRequestWrapper(HttpServletRequest request) throws IOException {
super(request);
StringBuilder sb = new StringBuilder();
InputStream ins = request.getInputStream();
BufferedReader isr = null;
try{
if(ins != null){
isr = new BufferedReader(new InputStreamReader(ins));
char[] charBuffer = new char[128];
int readCount;
while((readCount = isr.read(charBuffer)) != -1){
sb.append(charBuffer,0,readCount);
}
}
}catch (IOException e){
throw e;
}finally {
if(isr != null) {
isr.close();
}
}
sb.toString();
body = sb.toString();
}
@Override
public BufferedReader getReader() {
return new BufferedReader(new InputStreamReader(this.getInputStream()));
}
@Override
public ServletInputStream getInputStream() {
final ByteArrayInputStream byteArrayIns = new ByteArrayInputStream(body.getBytes());
ServletInputStream servletIns = new ServletInputStream() {
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener readListener) {
}
@Override
public int read() {
return byteArrayIns.read();
}
};
return servletIns;
}
}
过滤器定义(ReadBodyHttpServletFilter.java)
package com.whale.finance.filter;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @author yanyq
* @desc 重复读取HttpServletRequest reader/inputstream
* @date 2023/7/27
*/
@Component
@WebFilter("/*")
public class ReadBodyHttpServletFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
RepeatedlyReadRequestWrapper requestWrapper = new RepeatedlyReadRequestWrapper(httpServletRequest);
filterChain.doFilter(requestWrapper, httpServletResponse);
}
@Override
public void destroy() {
}
}
进行验证测试
/**
* 测试request.getReader
*/
@PostMapping("/list")
public void test(HttpServletRequest request) {
request.getReader(); // 不报错
return;
}
Reference
https://blog.csdn.net/feeltouch/article/details/103275416