web-dev-qa-db-ja.com

HttpServletRequest入力ストリームが空なのはなぜですか?

リクエスト入力ストリームから入力を読み取り、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文字列を送信していると確信しています。何が問題なのですか?負荷がかかった状態でのJetty7の予想される動作ですか?

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)
18
Usman Ismail

事前に消費されている場合は空になります。これは、HttpServletRequestgetParameter()getParameterValues()getParameterMap()getReader()などを呼び出すたびに暗黙的に実行されます。 getInputStream()を呼び出す前に、それ自体でリクエスト本文から情報を収集する必要がある種類のメソッドを呼び出さないようにしてください。サーブレットがそれを行っていない場合は、同じURLパターンにマップされているサーブレットフィルターのチェックを開始します。


更新:これはGAE1.5固有のようです。も参照してください

彼らがそれを修正するまで、私は解決策/回避策がないのではないかと心配しています。 tryを使用して、Filter内で使用できるかどうかを確認し、使用できる場合は、コピーしてリクエスト属性として保存できます。ただし、これはmight一部のGAEサーブレットによる以降の処理に影響します。

14
BalusC

SpringBootアプリケーションの実行でも同様の問題が発生しました。私のSpringBootアプリは、リクエスト本文を読み取って処理する単純なDispatcherサーブレットです。

私の場合、curlコマンドラインが_-d {some-data}_を使用し、特定のコンテンツタイプを設定しない場合、クライアント(curl)はapplication/x-www-form-urlencodedのコンテンツタイプヘッダーを設定します_-Hcontent-type=some-other-media-type_経由のヘッダー。

SpringBootが実行するApacheCatalinaサーブレットエンジン内で、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 (....)
_

これは本体を消費します。したがって、私の場合、myサーブレットは、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;
}
_

(これは解決に使用されました 別の問題

15
djb

Jetty 6.1.15ではリクエストInputStreamが常に空であるという問題があり、「Content-Type」ヘッダーがないか間違っていることが原因であることがわかりました。

HttpUrlConnectionを使用して別のJavaプログラムでリクエストを生成します。Content-Typeヘッダーを明示的に設定しなかった場合、request.getInputStream()によって返されるInputStreamは受信プログラムは常に空でした。コンテンツタイプを「binary/octet-stream」に設定すると、リクエストのInputStreamに正しいデータが含まれていました。

getInputStream()の前にリクエストオブジェクトで呼び出される唯一のメソッドはgetContentLength()です。

6
Tobias Lott

投稿でこの問題が発生しました。パラメータを読み取る前に、まず入力ストリームを読み取り、キャッシュに入れることで解決しました。それはトリックをするようでした

0
keesp

Spring Boot2.2.1プロジェクトでorg.springframeworkのデバッグログを有効にして、spring-webmvc 5.2.1を使用すると、問題が発生しました。

これは、Content-Typeapplication/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");
        }
...

ソース

私は結局 問題 を報告し、代わりにリクエストのコンテンツタイプを変更しました。

体系的なアプローチは次のとおりです。

  1. コンテナのソースコード、または少なくともそのWebパーツ(見つけるのが難しい場合があります)を取得し、IDEにインポートします。
  2. HttpServletRequest->getInputStream()が呼び出される前のコードにブレークポイントを作成します。
  3. HttpServletRequest->getInputStream()メソッドにステップインすると、... Implクラスになります。
  4. そのgetInputStream()実装、またはそのread()メソッドに新しいブレークポイントを設定します。
  5. テスト呼び出しを繰り返して、データを消費しているものを確認します。
0
radzimir

この問題の原因となるバグがあったmod_jk1.2.39を使用していました。 1.2.40にアップデートした後、動作を開始しました。

0
Sarel Botha