应用场景
如果你打算通过 javax.servlet.Filter 来拦截 POST 请求,并想通过 ServletInputStream 获取 body 的内容,等请求到达实际API时会报错:stream closed
:::info
那是因为在Java中的 Stream 流只能使用一次,再次使用时该流已经被关闭了。
:::
解决方案1-Spring自带Wrapper
直接使用 org.springframework.web.util 包下的 ContentCachingRequestWrapper 类即可。
BodyReadFilter
import java.io.IOException;import javax.servlet.Filter;import javax.servlet.FilterChain;import javax.servlet.ServletException;import javax.servlet.ServletRequest;import javax.servlet.ServletResponse;import javax.servlet.http.HttpServletRequest;import org.springframework.stereotype.Component;import org.apache.commons.io.IOUtils;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.web.util.ContentCachingRequestWrapper;@Componentpublic class BodyReadFilter implements Filter {private static final Logger LOGGER = LoggerFactory.getLogger(BodyReadFilter.class);public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)throws IOException, ServletException {ContentCachingRequestWrapper wrappedRequest = new ContentCachingRequestWrapper((HttpServletRequest) servletRequest);LOGGER.info("The body of the request was {}", IOUtils.toString(wrappedRequest.getInputStream()));filterChain.doFilter(wrappedRequest, servletResponse);}}
解决方案2-自己实现
MultiReadHttpServletRequest
import java.io.BufferedReader;import java.io.ByteArrayInputStream;import java.io.ByteArrayOutputStream;import java.io.IOException;import java.io.InputStreamReader;import javax.servlet.ReadListener;import javax.servlet.ServletInputStream;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletRequestWrapper;import org.apache.commons.io.IOUtils;/*via https://stackoverflow.com/a/36619972/2257038 and https://stackoverflow.com/a/30748533/2257038*/public class MultiReadHttpServletRequest extends HttpServletRequestWrapper {private ByteArrayOutputStream cachedBytes;public MultiReadHttpServletRequest(HttpServletRequest request) {super(request);}@Overridepublic ServletInputStream getInputStream() throws IOException {if (cachedBytes == null) cacheInputStream();return new CachedServletInputStream(cachedBytes.toByteArray());}@Overridepublic BufferedReader getReader() throws IOException {return new BufferedReader(new InputStreamReader(getInputStream()));}private void cacheInputStream() throws IOException {/* Cache the inputstream in order to read it multiple times. For* convenience, I use apache.commons IOUtils*/cachedBytes = new ByteArrayOutputStream();IOUtils.copy(super.getInputStream(), cachedBytes);}/* An inputstream which reads the cached request body */public static class CachedServletInputStream extends ServletInputStream {private final ByteArrayInputStream buffer;public CachedServletInputStream(byte[] contents) {this.buffer = new ByteArrayInputStream(contents);}@Overridepublic int read() {return buffer.read();}@Overridepublic boolean isFinished() {return buffer.available() == 0;}@Overridepublic boolean isReady() {return true;}@Overridepublic void setReadListener(ReadListener listener) {throw new RuntimeException("Not implemented");}}}
BodyReadFilter
import java.io.IOException;import javax.servlet.Filter;import javax.servlet.FilterChain;import javax.servlet.ServletException;import javax.servlet.ServletRequest;import javax.servlet.ServletResponse;import javax.servlet.http.HttpServletRequest;import org.springframework.stereotype.Component;import org.apache.commons.io.IOUtils;import org.slf4j.Logger;import org.slf4j.LoggerFactory;@Componentpublic class BodyReadFilter implements Filter {private static final Logger LOGGER = LoggerFactory.getLogger(BodyReadFilter.class);public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)throws IOException, ServletException {MultiReadHttpServletRequest wrappedRequest = new MultiReadHttpServletRequest((HttpServletRequest) servletRequest);LOGGER.info("The body of the request was {}", IOUtils.toString(wrappedRequest.getInputStream()));filterChain.doFilter(wrappedRequest, servletResponse);}}
参考文章:Reading a Servlet/Spring Request Body Multiple Times
其他相关文章:https://juejin.im/post/6858037733776949262
