Tomcat 过滤器对请求输入流产生不利影响(输入流为空)

发布于 2024-11-25 14:56:36 字数 6036 浏览 1 评论 0原文

我在我的应用程序中添加了一个过滤器,它只记录有关请求的某些内容。我的一些 servlet 读取 ServletRequest#getInputStream。添加此过滤器后,那些从 ServletRequest#getInputStream 不再起作用,因为输入流为空。只需从我的 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);

}

结论

根据我通过谷歌搜索读到的内容,以下任意一项如果首先调用,方法会将 getInputStream 渲染为空:

  • getParameter
  • getParameterNames
  • getParameterValues
  • getParameterMap

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

这里有一些来源

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

I've added a filter to my application which simply logs certain things about a request. Some of my servlets read from ServletRequest#getInputStream. Since adding this filter, those servlets that read from ServletRequest#getInputStream no longer work as the input stream is empty. Disabling the filter by simply commenting it out from my web.xml resolves the issue.

Why is this happening and is there a way to use a filter without it messing up the ServletRequest#getInputStream?

The filter is actually Tomcat's RequestDumperFilter included in one of its example web apps. I'll only include the doFilter method as that is the important part. If you want to see the entire thing, I've put it up on 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);

}

Conclusion

From what I've read by Googling, any of the following methods will render getInputStream empty if called first:

  • getParameter
  • getParameterNames
  • getParameterValues
  • getParameterMap

Thanks SimoneGianni for pointing me in the right direction:

Here are some sources

This person actually had a similar issue and created his own wrapper class as a work-around.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

此刻的回忆 2024-12-02 14:56:37

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

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

If you call getParameters, getParameterNames and similar methods, you COULD interfere with getInputStream or getReader. This isn't stated clearly enough in servlet documentation, but there are some warnings about the opposite (getInputStream interfering with getParameter) in the official servlet javadocs (since 1.3, see http://download.oracle.com/javaee/1.3/api/javax/servlet/ServletRequest.html#getParameter(java.lang.String) )

Are you seeing this problem on POSTs? Since POST requests encode parameters as request body, to read the parameters you actually have to consume (the container does this for you) the input stream.

月下凄凉 2024-12-02 14:56:37

哇。这是一件好事。我敢打赌,RequestDumper 必须解析整个输入流才能转储请求,这就是输入流为空的原因。我认为您不能在任何计划执行 getInputStream 的 Servlet 前面使用 RequestDumper。不过,从现在起我对你的选择感到茫然。也许可以在 chain.doFilter(request, response); 之后直接从 HttpRequest 中转储您感兴趣的参数。称呼。

Wow. That's a good one. I'm betting that the RequestDumper has to parse the whole InputStream to, well, dump the request and that is why the InputStream is empty. I'm thinking that you can't RequestDumper in front of any Servlet that is planning on doing getInputStream. I am at a loss for what your options are from this point, though. Maybe dump the parameters you are interested in directly from the HttpRequest AFTER the chain.doFilter(request, response); call.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文