Почему входной поток HttpServletRequest пуст?

У меня есть этот код, где я читаю ввод из потока ввода запроса и использую JacksonMapper для преобразования в POJO. Он работает в пристани 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)

6 ответов

Решение

Он будет пустым, если он уже употреблен заранее. Это будет сделано неявно, когда вы звоните getParameter(), getParameterValues(), getParameterMap(), getReader()и т. д. на HttpServletRequest, Убедитесь, что вы не вызываете ни один из тех методов, которые сами по себе должны собрать информацию из тела запроса перед вызовом. getInputStream(), Если ваш сервлет этого не делает, тогда начните проверять фильтры сервлета, которые сопоставлены с тем же шаблоном URL.


Обновление: это, кажется, специфично для GAE 1.5. Смотрите также

Я боюсь, что нет решения / обходного пути, пока они не исправят это. Вы можете попробовать проверить, доступен ли он внутри Filter и если да, то скопируйте и сохраните его как атрибут запроса. Но это может повлиять на дальнейшую обработку некоторыми сервлетами GAE.

У меня была похожая проблема при запуске приложения Spring Boot. My Spring Boot - простое приложение Dispatcher сервлет, который читает тело запроса и обрабатывает его.

В моем случае клиент (curl) устанавливает заголовок типа содержимого application/x-www-form-urlencoded, если в командной строке curl используется -d {some-data} и не устанавливает определенный заголовок типа контента через -Hcontent-type=some-other-media-type,

Внутри сервлет-движка Apache Catalina, запускаемого Spring Boot, 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 (....)

который будет потреблять тело. Так что в моем случае, хотя мой сервлет ничего не делает, кроме как вызвать 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;
}

(который был использован для решения другой проблемы)

У меня была проблема с тем, что мой запрос InputStream всегда был пуст в Jetty 6.1.15, и я обнаружил, что это вызвано отсутствующим или неправильным заголовком "Content-Type".

Я генерирую запросы в другой Java-программе с HttpUrlConnection. Когда я не установил заголовок Content-Type явно, InputStream вернулся request.getInputStream() в принимающей программе всегда было пусто. Когда я устанавливаю тип контента "двоичный / октет-поток", InputStream запроса содержал правильные данные.

Единственный метод, который вызывается для объекта запроса до getInputStream() является getContentLength(),

Я использовал mod_jk 1.2.39, в котором была ошибка, вызвавшая эту проблему. После обновления до 1.2.40 все заработало.

Системный подход это:

  1. Получите исходный код для своего контейнера или, по крайней мере, его веб-часть (может быть трудно найти), импортируйте ее в IDE.
  2. Сделать точку останова в вашем коде, где раньше HttpServletRequest->getInputStream() называется.
  3. Шаг в HttpServletRequest->getInputStream() метод, теперь вы в каком-то...Impl классе.
  4. Установить новую точку останова в этом getInputStream() реализация, или даже в его read() метод.
  5. Повторите тестовый вызов и посмотрите, что потребляет ваши данные.

У меня была эта проблема с постом. Я решил это ПЕРВЫМ чтением входного потока и помещением его в кеш, перед чтением параметров. Это, казалось, добилось цели

У меня возникла проблема при включении ведения журнала отладки для org.springframework в проекте Spring Boot 2.2.1 и, следовательно, с использованием spring-webmvc 5.2.1.

Это вызвано журналированием запроса карты параметров, которая считывает входной поток, если Content-Type является application/x-www-form-urlencoded. Думаю, с этим связан весенний выпуск.

См. Следующий код, вызывающий проблему.

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");
        }
...

источник

В итоге я сообщил о проблеме и вместо этого изменил тип содержимого в запросе.

Другие вопросы по тегам