【问题标题】:Why is HttpServletRequest inputstream empty?为什么 HttpServletRequest 输入流为空?
【发布时间】:2012-01-21 07:05:05
【问题描述】:

我有这段代码,我从请求输入流中读取输入并使用 JacksonMapper 转换为 POJO。它在带有 guice 支持的 jetty 7 容器中运行。

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

    try {
        RequestType requestType = mapper.readValue(req.getInputStream(), RequestType.class);
    } Catch(Exception ex) {
        ....
    }
}

但是,有时在负载下会引发以下异常。我检查了我的客户,我确信它发送了一个有效的 json 字符串。出了什么问题?这是 Jetty 7 在负载下的预期行为吗?

java.io.EOFException: No content to map to Object due to end of input
    at org.codehaus.jackson.map.ObjectMapper._initForReading(ObjectMapper.java:2433)
    at org.codehaus.jackson.map.ObjectMapper._readMapAndClose(ObjectMapper.java:2385)
    at org.codehaus.jackson.map.ObjectMapper.readValue(ObjectMapper.java:1637)
    at com.ea.wsop.user.LoginServlet.processRequest(LoginServlet.java:69)
    at com.ea.wsop.user.LoginServlet.doPost(LoginServlet.java:63)
    at com.ea.wsop.user.LoginServlet$$EnhancerByGuice$$a91c2ebd.CGLIB$doPost$0(<generated>)
    at com.ea.wsop.user.LoginServlet$$EnhancerByGuice$$a91c2ebd$$FastClassByGuice$$c6f479ee.invoke(<generated>)
    at com.google.inject.internal.cglib.proxy.$MethodProxy.invokeSuper(MethodProxy.java:228)
    at com.google.inject.internal.InterceptorStackCallback$InterceptedMethodInvocation.proceed(InterceptorStackCallback.java:72)
    at com.ea.monitor.MethodExecutionTimer.invoke(MethodExecutionTimer.java:130)
    at com.google.inject.internal.InterceptorStackCallback$InterceptedMethodInvocation.proceed(InterceptorStackCallback.java:72)
    at com.google.inject.internal.InterceptorStackCallback.intercept(InterceptorStackCallback.java:52)
    at com.ea.wsop.user.LoginServlet$$EnhancerByGuice$$a91c2ebd.doPost(<generated>)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
    at com.ea.wsop.user.LoginServlet$$EnhancerByGuice$$a91c2ebd.CGLIB$service$8(<generated>)
    at com.ea.wsop.user.LoginServlet$$EnhancerByGuice$$a91c2ebd$$FastClassByGuice$$c6f479ee.invoke(<generated>)
    at com.google.inject.internal.cglib.proxy.$MethodProxy.invokeSuper(MethodProxy.java:228)
    at com.google.inject.internal.InterceptorStackCallback$InterceptedMethodInvocation.proceed(InterceptorStackCallback.java:72)
    at com.ea.monitor.MethodExecutionTimer.invoke(MethodExecutionTimer.java:130)
    at com.google.inject.internal.InterceptorStackCallback$InterceptedMethodInvocation.proceed(InterceptorStackCallback.java:72)
    at com.google.inject.internal.InterceptorStackCallback.intercept(InterceptorStackCallback.java:52)
    at com.ea.wsop.user.LoginServlet$$EnhancerByGuice$$a91c2ebd.service(<generated>)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:820)
    at com.ea.wsop.user.LoginServlet$$EnhancerByGuice$$a91c2ebd.CGLIB$service$9(<generated>)
    at com.ea.wsop.user.LoginServlet$$EnhancerByGuice$$a91c2ebd$$FastClassByGuice$$c6f479ee.invoke(<generated>)
    at com.google.inject.internal.cglib.proxy.$MethodProxy.invokeSuper(MethodProxy.java:228)
    at com.google.inject.internal.InterceptorStackCallback$InterceptedMethodInvocation.proceed(InterceptorStackCallback.java:72)
    at com.ea.monitor.MethodExecutionTimer.invoke(MethodExecutionTimer.java:130)
    at com.google.inject.internal.InterceptorStackCallback$InterceptedMethodInvocation.proceed(InterceptorStackCallback.java:72)
    at com.google.inject.internal.InterceptorStackCallback.intercept(InterceptorStackCallback.java:52)
    at com.ea.wsop.user.LoginServlet$$EnhancerByGuice$$a91c2ebd.service(<generated>)
    at com.google.inject.servlet.ServletDefinition.doService(ServletDefinition.java:263)

【问题讨论】:

    标签: java servlets jetty guice


    【解决方案1】:

    如果事先已经消耗,它将是空的。每当您在HttpServletRequest 上调用getParameter()getParameterValues()getParameterMap()getReader() 等时,都会隐式执行此操作。确保在调用getInputStream() 之前,您不调用任何本身需要从请求正文中收集信息的方法。如果您的 servlet 没有这样做,请开始检查映射到相同 URL 模式的 servlet 过滤器。


    更新:这似乎是 GAE 1.5 特有的。另请参阅

    恐怕在他们修复之前没有解决方案/解决方法。您可以尝试检查它是否在Filter 中可用,如果是,则将其复制并存储为请求属性。但这可能会影响某些 GAE servlet 的进一步处理。

    【讨论】:

    • 我的代码中没有任何东西在消耗它,但 Guice 可能正在做一些事情。
    • 您可以尝试改用getQueryString(),我只是不确定GAE如何处理“语法无效”的查询字符串。
    • 这是一个发布请求,我正在从那里提取数据。
    • 你永远不会知道 GAE 是个奇怪的野兽。至少,我使用“gae request getinputstream”搜索并发现了几个类似的问题和错误报告。我将其包含在我的答案中。
    • 这是在 jetty 7 的 ec2 实例上运行,而不是应用引擎。
    【解决方案2】:

    在 Jetty 6.1.15 中我的请求 InputStream 总是为空的问题,发现这是由于缺少或错误的“Content-Type”标头引起的。

    我使用 HttpUrlConnection 在另一个 Java 程序中生成请求。当我没有显式设置 Content-Type 标头时,接收程序中request.getInputStream() 返回的InputStream 始终为空。当我将内容类型设置为“二进制/八位字节流”时,请求的InputStream 包含正确的数据。

    getInputStream() 之前对请求对象调用的唯一方法是getContentLength()

    【讨论】:

      【解决方案3】:

      我使用的是 mod_jk 1.2.39,它有一个导致此问题的错误。更新到 1.2.40 后,它开始工作了。

      【讨论】:

        【解决方案4】:

        我在运行 Spring Boot 应用程序时遇到了类似的问题。我的 Spring Boot 应用程序是一个简单的 Dispatcher servlet,它读取请求正文并对其进行处理。

        在我的例子中,如果 curl 命令行使用 -d {some-data} 并且没有通过-Hcontent-type=some-other-media-type.

        在 Spring Boot 运行的 Apache Catalina servlet 引擎中,Request 类在 parseParameters() 中进行以下测试

                if (!("application/x-www-form-urlencoded".equals(contentType))) {
                    success = true;
                    return;
                }
        

        对于其他 content-type 值,Request 在此处返回,完成。

        但是,如果内容类型匹配 application/x-www-form-urlencodedRequest 继续:

            try {
               if (readPostBody(formData, len) != len) {           
                    parameters.setParseFailedReason(FailReason.REQUEST_BODY_INCOMPLETE);
                    return;
                }
            } catch (....)
        

        这将消耗身体。因此,在我的情况下,即使 my servlet 除了调用 request.getInputStream() 并尝试从中调用 read() 之外什么都不做,但已经太晚了 - 运行时 Request 已经读取了输入并执行了不缓冲或未读它。唯一的解决方法是设置不同的Content-Type

        罪魁祸首是 OrderedHiddenHttpMethodFilter(HiddenHttpMethodFilter).doFilterInternal(HttpServletRequest, HttpServletResponse, FilterChain)第70行

        它正在寻找"_method" 查询参数。

        我可以通过添加禁用过滤器

        @Bean
        public FilterRegistrationBean registration(HiddenHttpMethodFilter filter) {
            FilterRegistrationBean registration = new FilterRegistrationBean(filter);
            registration.setEnabled(false);
            return registration;
        }
        

        (用于解决another problem

        【讨论】:

        • 这是一个很棒的发现,否则,控制器的 Post 处理程序中的 HttpServletRequest 总是无故返回空。
        • 这是一个艰难的过程,2 小时试图找出帖子实体为空的原因。非常感谢!
        【解决方案5】:

        我的帖子遇到了这个问题。我通过在读取参数之前首先读取输入流并将其放入缓存来解决它。这似乎可以解决问题

        【讨论】:

          【解决方案6】:

          系统的方法是:

          1. 获取您的容器的源代码,或者至少是它的 Web 部件(可能很难找到),导入您的 IDE。
          2. 在调用HttpServletRequest-&gt;getInputStream() 之前的代码中设置断点。
          3. 进入HttpServletRequest-&gt;getInputStream() 方法,现在您在某个...Impl 类中。
          4. getInputStream() 实现中设置新断点,甚至在其 read() 方法中设置。
          5. 重复测试调用,看看是什么消耗了您的数据。

          【讨论】:

            【解决方案7】:

            我在 Spring Boot 2.2.1 项目中为 org.springframework 启用调试日志记录时遇到了问题,因此使用了 spring-webmvc 5.2.1。

            这是由参数映射的请求日志记录引起的,如果Content-Typeapplication/x-www-form-urlencoded,它会读取输入流。我相信this spring issue与它有关。

            请参阅以下导致问题的代码。

            private void logRequest(HttpServletRequest request) {
                LogFormatUtils.traceDebug(logger, traceOn -> {
                    String params;
                    if (isEnableLoggingRequestDetails()) {
                        params = request.getParameterMap().entrySet().stream()
                                .map(entry -> entry.getKey() + ":" + Arrays.toString(entry.getValue()))
                                .collect(Collectors.joining(", "));
                    }
                    else {
                        params = (request.getParameterMap().isEmpty() ? "" : "masked");
                    }
            ...
            

            source

            我最终报告了an issue,并改为更改请求中的内容类型。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2015-03-22
              • 1970-01-01
              • 2015-12-07
              • 2014-12-19
              • 1970-01-01
              • 1970-01-01
              • 2017-10-04
              相关资源
              最近更新 更多