为什么 HttpServletRequest 输入流是空的?

2022-09-02 03:05:00

我有这个代码,我从请求输入流中读取输入,并使用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)

答案 1

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

在我的例子中,如果 curl 命令行使用并且未通过 设置特定的内容类型标头,则客户端 () 设置 application/x-www-form-urlencoded 的内容类型标头。curl-d {some-data}-Hcontent-type=some-other-media-type

在Spring Boot运行的Apache Catalina servlet引擎中,该类在RequestparseParameters()

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

对于其他值,返回到此处,完成。content-typeRequest

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

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

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

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

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

我能够通过添加

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

(用于解决另一个问题))


答案 2

如果事先已经消耗,它将是空的。这将隐式完成,每当您在 上调用 、 、 、 等。请确保不要调用任何一种方法,这些方法本身需要在调用 之前从请求正文中收集信息。如果你的 servlet 没有这样做,那么开始检查映射在同一 URL 模式上的 servlet 过滤器。getParameter()getParameterValues()getParameterMap()getReader()HttpServletRequestgetInputStream()


更新:这似乎是特定于 GAE 1.5 的。另请参见

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


推荐