java拦截器获取POST请求体后Controller异常Required request body is missing OR Stream closed
解决办法参考文档:https://blog.csdn.net/qierkang/article/details/88544691
springboot拦截器获取POST请求体后导致Controller中@RequestBody参数异常Required request body is missing OR Stream closed.
1.为什么会报这个错?
因为http的body只能读取一次。
2.为什么body设计为只能读取一次?
A.由于我们获取POST请求参数的时候,是通过读取request的IO流来实现的,一旦读取了那么流关闭后,后续就用不了了。
B.假如别人上传1T的文件,然后Java先全部读取到内存,服务器直接就挂了……所以不确定最大能多大的东西,默认都是流式处理,要缓存反复读。
解决办法:
1.写一个可重复读的Request包装类
import org.springframework.http.HttpMethod; 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.*; /** * 可重复读的Request包装类 */ @Slf4j public class RepeatedlyRequestWrapper extends HttpServletRequestWrapper { /** * 缓存下来的HTTP body */ private byte[] body; public RepeatedlyRequestWrapper(HttpServletRequest request) throws IOException { super(request); body = StreamUtils.copyToByteArray(request.getInputStream()); } /** * 重新包装输入流 * * @return * @throws IOException */ @Override public ServletInputStream getInputStream() throws IOException { InputStream bodyStream = new ByteArrayInputStream(body); return new ServletInputStream() { @Override public int read() throws IOException { return bodyStream.read(); } /** * 下面的方法一般情况下不会被使用,如果你引入了一些需要使用ServletInputStream的外部组件,可以重点关注一下。 * @return */ @Override public boolean isFinished() { return false; } @Override public boolean isReady() { return true; } @Override public void setReadListener(ReadListener readListener) { } }; } @Override public BufferedReader getReader() throws IOException { InputStream bodyStream = new ByteArrayInputStream(body); return new BufferedReader(new InputStreamReader(getInputStream())); } }
2.添加处理可重复读request的过滤器(过滤器记得开启生效)
import com.kuailaimi.security.client.util.RepeatedlyRequestWrapper; import org.apache.commons.lang3.StringUtils; import org.springframework.http.MediaType; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import java.io.IOException; /** * 处理可重复读request的过滤器 */ public class RepeatedlyReadFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { //将request可重复读的包装类传递下去 ServletRequest requestWrapper = null; if (request instanceof HttpServletRequest) { HttpServletRequest httpServletRequest = (HttpServletRequest) request; requestWrapper = new RepeatedlyRequestWrapper(httpServletRequest); } if (null == requestWrapper) { chain.doFilter(request, response); } else { chain.doFilter(requestWrapper, response); } } @Override public void destroy() { } }
3.在拦截器中使用
if (request instanceof RepeatedlyRequestWrapper) { RepeatedlyRequestWrapper repeatedlyRequest = (RepeatedlyRequestWrapper) request; String body = new String(StreamUtils.copyToByteArray(repeatedlyRequest.getInputStream()), repeatedlyRequest.getCharacterEncoding()); }