Почему входной поток 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. Смотрите также
- http://code.google.com/p/googleappengine/issues/detail?id=5161
- http://code.google.com/p/googleappengine/issues/detail?id=5898
Я боюсь, что нет решения / обходного пути, пока они не исправят это. Вы можете попробовать проверить, доступен ли он внутри 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 все заработало.
Системный подход это:
- Получите исходный код для своего контейнера или, по крайней мере, его веб-часть (может быть трудно найти), импортируйте ее в IDE.
- Сделать точку останова в вашем коде, где раньше
HttpServletRequest->getInputStream()
называется. - Шаг в
HttpServletRequest->getInputStream()
метод, теперь вы в каком-то...Impl классе. - Установить новую точку останова в этом
getInputStream()
реализация, или даже в егоread()
метод. - Повторите тестовый вызов и посмотрите, что потребляет ваши данные.
У меня была эта проблема с постом. Я решил это ПЕРВЫМ чтением входного потока и помещением его в кеш, перед чтением параметров. Это, казалось, добилось цели
У меня возникла проблема при включении ведения журнала отладки для 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");
}
...
В итоге я сообщил о проблеме и вместо этого изменил тип содержимого в запросе.