为什么HttpServletRequest输入流为空?

时间:2011-12-15 15:34:53

标签: java servlets jetty guice

我有这个代码,我从请求输入流中读取输入并使用JacksonMapper转换为POJO。它在带有guice支撑的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)

7 个答案:

答案 0 :(得分:14)

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

在我的情况下,如果curl命令行使用curl并且未设置特定内容,则客户端(-d {some-data})设置application / x-www-form-urlencoded的内容类型标头-type标题来自-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-urlencoded,则Request会继续:

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

消耗身体。所以在我的情况下,即使我的 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

答案 1 :(得分:12)

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


更新:这似乎是GAE 1.5特有的。另见

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

答案 2 :(得分:6)

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

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

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

答案 3 :(得分:0)

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

答案 4 :(得分:0)

我的帖子出现了这个问题。在读取参数之前,我通过FIRST读取输入流并将其放入缓存中解决了这个问题。这似乎可以解决问题

答案 5 :(得分:0)

系统的方法是:

  1. 获取容器的源代码,或者至少将其作为Web部件(很难找到),然后将其导入IDE中。
  2. 在代码中在调用HttpServletRequest->getInputStream()之前的地方设置断点。
  3. 进入HttpServletRequest->getInputStream()方法,现在您处于... Impl类中。
  4. 在该getInputStream()约束中,甚至在其read()方法中,设置一个新的断点。
  5. 重复测试电话,查看消耗数据的原因。

答案 6 :(得分:0)

当在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,并更改了请求中的内容类型。