Http Servlet 请求在读取一次后从 POST 正文中丢失参数
我正在尝试在Java Servlet过滤器中访问两个http请求参数,这里没有什么新东西,但惊讶地发现这些参数已经被消耗了!因此,它在过滤器链中不再可用。
似乎只有当参数出现在POST请求正文中(例如,表单提交)中时,才会发生这种情况。
有没有办法读取参数而不使用它们?
到目前为止,我只找到了这个参考:使用 request.getParameter 的 Servlet Filter 丢失了表单数据。
谢谢!
我正在尝试在Java Servlet过滤器中访问两个http请求参数,这里没有什么新东西,但惊讶地发现这些参数已经被消耗了!因此,它在过滤器链中不再可用。
似乎只有当参数出现在POST请求正文中(例如,表单提交)中时,才会发生这种情况。
有没有办法读取参数而不使用它们?
到目前为止,我只找到了这个参考:使用 request.getParameter 的 Servlet Filter 丢失了表单数据。
谢谢!
顺便说一句,解决此问题的另一种方法是不使用筛选器链,而是构建自己的拦截器组件,也许使用可以对解析的请求正文进行操作的方面。它也可能更有效,因为您只需将请求转换为您自己的模型对象一次。InputStream
但是,我仍然认为希望多次读取请求正文是合理的,尤其是在请求在筛选器链中移动时。我通常会将筛选器链用于某些操作,这些操作希望保留在 HTTP 层,与服务组件分离。
正如Will Hartung所建议的,我通过扩展,消耗请求和基本上缓存字节来实现这一点。HttpServletRequestWrapper
InputStream
public class MultiReadHttpServletRequest extends HttpServletRequestWrapper {
private ByteArrayOutputStream cachedBytes;
public MultiReadHttpServletRequest(HttpServletRequest request) {
super(request);
}
@Override
public ServletInputStream getInputStream() throws IOException {
if (cachedBytes == null)
cacheInputStream();
return new CachedServletInputStream();
}
@Override
public 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 input stream which reads the cached request body */
private static class CachedServletInputStream extends ServletInputStream {
private final ByteArrayInputStream buffer;
public CachedServletInputStream(byte[] contents) {
this.buffer = new ByteArrayInputStream(contents);
}
@Override
public int read() {
return buffer.read();
}
@Override
public boolean isFinished() {
return buffer.available() == 0;
}
@Override
public boolean isReady() {
return true;
}
@Override
public void setReadListener(ReadListener listener) {
throw new RuntimeException("Not implemented");
}
}
}
现在,通过在通过筛选器链传递原始请求之前包装原始请求,可以多次读取请求正文:
public class MyFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
/* wrap the request in order to read the inputstream multiple times */
MultiReadHttpServletRequest multiReadRequest = new MultiReadHttpServletRequest((HttpServletRequest) request);
/* here I read the inputstream and do my thing with it; when I pass the
* wrapped request through the filter chain, the rest of the filters, and
* request handlers may read the cached inputstream
*/
doMyThing(multiReadRequest.getInputStream());
//OR
anotherUsage(multiReadRequest.getReader());
chain.doFilter(multiReadRequest, response);
}
}
此解决方案还允许您通过这些方法多次读取请求正文,因为底层调用是 ,这当然会读取缓存的请求 。getParameterXXX
getInputStream()
InputStream
编辑
对于较新版本的界面。您需要提供更多方法的实现,例如 等。请按照下面评论中提供的内容来参考此问题。ServletInputStream
isReady
setReadListener
我知道我迟到了,但这个问题仍然与我有关,这篇SO帖子是Google中最受欢迎的文章之一。我将继续发布我的解决方案,希望其他人可以节省几个小时。
在我的情况下,我需要用他们的身体记录所有请求和响应。使用Spring Framework,答案实际上非常简单,只需使用ContentCachingRequestWrapper和ContentCachingResponseWrapper。
import org.springframework.web.util.ContentCachingRequestWrapper;
import org.springframework.web.util.ContentCachingResponseWrapper;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class LoggingFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void destroy() {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
ContentCachingRequestWrapper requestWrapper = new ContentCachingRequestWrapper((HttpServletRequest) request);
ContentCachingResponseWrapper responseWrapper = new ContentCachingResponseWrapper((HttpServletResponse) response);
try {
chain.doFilter(requestWrapper, responseWrapper);
} finally {
String requestBody = new String(requestWrapper.getContentAsByteArray());
String responseBody = new String(responseWrapper.getContentAsByteArray());
// Do not forget this line after reading response content or actual response will be empty!
responseWrapper.copyBodyToResponse();
// Write request and response body, headers, timestamps etc. to log files
}
}
}