【问题标题】:Tomcat Filter adversely affecting Request Input Stream (input stream empty)Tomcat过滤器对请求输入流产生不利影响(输入流为空)
【发布时间】:2011-10-10 11:14:34
【问题描述】:

我在我的应用程序中添加了一个过滤器,它只记录有关请求的某些内容。我的一些 servlet 读取自 ServletRequest#getInputStream。由于添加了这个过滤器,那些从ServletRequest#getInputStream 读取的servlet 不再工作,因为输入流是空的。通过简单地从我的web.xml 中注释掉过滤器来禁用过滤器即可解决问题。

为什么会发生这种情况?有没有办法使用过滤器而不弄乱ServletRequest#getInputStream

过滤器实际上是 Tomcat 的 RequestDumperFilter,包含在其示例 Web 应用程序之一中。我将只包括doFilter 方法,因为这是重要的部分。如果你想看整个事情,我已经把它放在PasteBin

/**
 * Time the processing that is performed by all subsequent filters in the
 * current filter stack, including the ultimately invoked servlet.
 *
 * @param request The servlet request we are processing
 * @param result The servlet response we are creating
 * @param chain The filter chain we are processing
 *
 * @exception IOException if an input/output error occurs
 * @exception ServletException if a servlet error occurs
 */
public void doFilter(ServletRequest request, ServletResponse response,
                     FilterChain chain)
throws IOException, ServletException {

    if (filterConfig == null)
    return;

// Render the generic servlet request properties
StringWriter sw = new StringWriter();
PrintWriter writer = new PrintWriter(sw);
writer.println("Request Received at " +
           (new Timestamp(System.currentTimeMillis())));
writer.println(" characterEncoding=" + request.getCharacterEncoding());
writer.println("     contentLength=" + request.getContentLength());
writer.println("       contentType=" + request.getContentType());
writer.println("            locale=" + request.getLocale());
writer.print("           locales=");
Enumeration locales = request.getLocales();
boolean first = true;
while (locales.hasMoreElements()) {
    Locale locale = (Locale) locales.nextElement();
    if (first)
        first = false;
    else
        writer.print(", ");
    writer.print(locale.toString());
}
writer.println();
Enumeration names = request.getParameterNames();
while (names.hasMoreElements()) {
    String name = (String) names.nextElement();
    writer.print("         parameter=" + name + "=");
    String values[] = request.getParameterValues(name);
    for (int i = 0; i < values.length; i++) {
        if (i > 0)
        writer.print(", ");
    writer.print(values[i]);
    }
    writer.println();
}
writer.println("          protocol=" + request.getProtocol());
writer.println("        remoteAddr=" + request.getRemoteAddr());
writer.println("        remoteHost=" + request.getRemoteHost());
writer.println("            scheme=" + request.getScheme());
writer.println("        serverName=" + request.getServerName());
writer.println("        serverPort=" + request.getServerPort());
writer.println("          isSecure=" + request.isSecure());

// Render the HTTP servlet request properties
if (request instanceof HttpServletRequest) {
    writer.println("---------------------------------------------");
    HttpServletRequest hrequest = (HttpServletRequest) request;
    writer.println("       contextPath=" + hrequest.getContextPath());
    Cookie cookies[] = hrequest.getCookies();
        if (cookies == null)
            cookies = new Cookie[0];
    for (int i = 0; i < cookies.length; i++) {
        writer.println("            cookie=" + cookies[i].getName() +
               "=" + cookies[i].getValue());
    }
    names = hrequest.getHeaderNames();
    while (names.hasMoreElements()) {
        String name = (String) names.nextElement();
    String value = hrequest.getHeader(name);
        writer.println("            header=" + name + "=" + value);
    }
    writer.println("            method=" + hrequest.getMethod());
    writer.println("          pathInfo=" + hrequest.getPathInfo());
    writer.println("       queryString=" + hrequest.getQueryString());
    writer.println("        remoteUser=" + hrequest.getRemoteUser());
    writer.println("requestedSessionId=" +
           hrequest.getRequestedSessionId());
    writer.println("        requestURI=" + hrequest.getRequestURI());
    writer.println("       servletPath=" + hrequest.getServletPath());
}
writer.println("=============================================");

// Log the resulting string
writer.flush();
filterConfig.getServletContext().log(sw.getBuffer().toString());

// Pass control on to the next filter
    chain.doFilter(request, response);

}

结论

根据我从Googling 阅读的内容,如果首先调用,以下任何方法都会使getInputStream 为空:

  • getParameter
  • getParameterNames
  • getParameterValues
  • getParameterMap

感谢 SimoneGianni 为我指明了正确的方向:

这里有一些来源

这个人实际上遇到了类似的问题,并创建了自己的包装类作为解决方法。

【问题讨论】:

  • 可以加一些过滤代码吗?
  • 当然。它实际上是 Tomcat 的 RequestDumperFilter,它包含在其中一个示例 Web 应用程序中。我会在上面的问题中弹出它。

标签: java tomcat6 inputstream servlet-filters


【解决方案1】:

如果您调用 getParameters、getParameterNames 和类似方法,您可能会干扰 getInputStream 或 getReader。这在 servlet 文档中没有明确说明,但在官方 servlet javadocs(从 1.3 开始,请参阅 http://download.oracle.com/javaee/1.3/api/javax/servlet/ServletRequest.html#getParameter(java.lang.String)中有一些相反的警告(getInputStream 干扰 getParameter)

您在 POST 上看到这个问题了吗?由于 POST 请求将参数编码为请求正文,因此要读取您实际必须使用的参数(容器为您执行此操作)输入流。

【讨论】:

  • 你可以绕过它,可能,通过使用包装的请求调用chain.doFilter,在该请求上“重建”输入流,使用某种管道流,或者只是一个大字节数组,但是小心如果你收到一个非常大的 POST 会发生什么,因为它会耗尽你的 RAM 并暴露于 DOS 攻击。
  • 很棒的信息!谢谢。是的,这个问题发生在 POST 请求上。那么通过拦截请求(通过过滤器),输入流在doFilter方法结束时被消耗还是丢弃?
  • 关于“重建”,你的意思是用输入流的内容写入输出流吗?
  • 从我在谷歌上看到的内容来看,如果首先调用以下任何方法,getInputStream 将呈现为空:getParametergetParameterNamesgetParameterValuesgetParameterMap
  • @John 是的,通常是这样,但实际上它取决于容器实现。
【解决方案2】:

哇。这是一个很好的。我打赌 RequestDumper 必须解析整个 InputStream 以转储请求,这就是 InputStream 为空的原因。我认为您不能在任何计划执行 getInputStream 的 Servlet 前面使用 RequestDumper。不过,从这一点来看,我不知道你的选择是什么。也许在 chain.doFilter(request, response); 之后直接从 HttpRequest 中转储您感兴趣的参数。打电话。

【讨论】:

    猜你喜欢
    • 2013-06-30
    • 1970-01-01
    • 2014-11-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-06-14
    • 1970-01-01
    • 2017-10-04
    相关资源
    最近更新 更多