2011-09-06 19 views
14

サーブレットにログを追加したいので、リクエストを表示してサーブレットに移動するためのFilterを作成しました。だから私はラッパーと解決策を見つけたが、それは動作しません。この問題を解決するためにjava.lang.IllegalStateException:この要求に対してgetReader()が既に呼び出されている

java.lang.IllegalStateException: getReader() has already been called for this request 
    at org.apache.catalina.connector.Request.getInputStream(Request.java:948) 
    at org.apache.catalina.connector.RequestFacade.getInputStream(RequestFacade.java:338) 
    at com.noelios.restlet.ext.servlet.ServletCall.getRequestEntityStream(ServletCall.java:190) 

:しかし残念ながら、私は例外をencouteredました。他にコードで使用/変更することはできますか?何か案は?

[MyHttpServletRequestWrapper]

public class MyHttpServletRequestWrapper extends HttpServletRequestWrapper 
{ 
    public MyHttpServletRequestWrapper(HttpServletRequest request) 
    { 
     super(request); 
    } 

    private String getBodyAsString() 
    { 
     StringBuffer buff = new StringBuffer(); 
     buff.append(" BODY_DATA START [ "); 
     char[] charArr = new char[getContentLength()]; 
     try 
     { 
      BufferedReader reader = new BufferedReader(getReader()); 
      reader.read(charArr, 0, charArr.length); 
      reader.close(); 
     } 
     catch (IOException e) 
     { 
      e.printStackTrace(); 
     } 
     buff.append(charArr); 
     buff.append(" ] BODY_DATA END "); 
     return buff.toString(); 
    } 

    public String toString() 
    { 
     return getBodyAsString(); 
    } 
} 

[myFilterを]

public class MyFilterimplements Filter 
{ 
    @Override 
    public void init(FilterConfig filterConfig) throws ServletException 
    { 
    } 

    @Override 
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException 
    { 
     final HttpServletRequest httpServletRequest = (HttpServletRequest) request; 
     final HttpServletResponse httpServletResponse = (HttpServletResponse) response; 

     final HttpServletRequestWrapper requestWrapper = new MyHttpServletRequestWrapper(httpServletRequest); 
     final String requestBody = requestWrapper.toString(); 

     chain.doFilter(request, response); 
    } 
} 

答えて

9

のRestletフレームワークは、順番にgetInputStream()を呼び出し、リクエストオブジェクトにgetRequestEntityStream()と呼ばれ、その要求にgetReader()を呼び出すとIllegalStateExceptionをスローしたように見えます。 getReader()とのgetInputStream()のためのサーブレットAPIのドキュメントは言う:ドキュメントから

public java.io.BufferedReader getReader() 
    ... 
    ... 
Throws: 
    java.lang.IllegalStateException - if getInputStream() method has been called on this request 

public ServletInputStream getInputStream() 
    ... 
    ... 
    Throws: 
    java.lang.IllegalStateException - if the getReader() method has already been called for this request 

我々がリクエストオブジェクトにgetReader両方()とのgetInputStream()を呼び出すことはできませんようです。あなたのラッパーにgetReader()ではなくgetInputStream()を使用することをお勧めします。

5

主な問題は、バイナリストリームと文字ストリームの両方で入力を読み取ることができないことです。入力ストリームがフィルタで呼び出され、サーブレット内で呼び出されたとしても、入力を読み取ることはできません。

3

サーブレットについては、これに関しては根本的に壊れていると言えます。この問題は、hereで概説されているように試してみることができますが、それは他のものが試して作業するときに他の不思議な問題を引き起こします。

彼は効果的に、要求を複製し、本文を読み取り、次にクローンされたクラスで、getReaderメソッドとgetInputStreamメソッドをオーバーライドして、すでに取得されたものを戻すことを提案します。

私がなってしまったコードは、このでした:我々は、ブラウザからファイルをアップロードすることは働いていなかったことを実現するまでとにかく

import javax.servlet.ServletInputStream; 
import javax.servlet.http.HttpServletRequest; 
import javax.servlet.http.HttpServletRequestWrapper; 
import java.io.*; 

//this class stops reading the request payload twice causing an exception 
public class WrappedRequest extends HttpServletRequestWrapper 
{ 
    private String _body; 
    private HttpServletRequest _request; 

    public WrappedRequest(HttpServletRequest request) throws IOException 
    { 
     super(request); 
     _request = request; 

     _body = ""; 
     try (BufferedReader bufferedReader = request.getReader()) 
     { 
      String line; 
      while ((line = bufferedReader.readLine()) != null) 
       _body += line; 
     } 
    } 

    @Override 
    public ServletInputStream getInputStream() throws IOException 
    { 
     final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(_body.getBytes()); 
     return new ServletInputStream() 
     { 
      public int read() throws IOException 
      { 
       return byteArrayInputStream.read(); 
      } 
     }; 
    } 

    @Override 
    public BufferedReader getReader() throws IOException 
    { 
     return new BufferedReader(new InputStreamReader(this.getInputStream())); 
    } 
} 

これは正常に動作しているように見えました。私は変更を二分し、これが原因であることを発見しました。

この記事のコメントの中には、パラメータを使用する方法をオーバーライドする必要があると言われていますが、これを行う方法は説明されていません。

この結果、2つの要求に違いがあるかどうかを確認しました。しかし、要求を複製した後は、同じセットのパラメータ(元の要求+クローン化されていないものもあります)と同一のヘッダーセットがありました。

しかし、何らかの形でリクエストが実行されていて、リクエストの理解がさらに悪化していました。私の場合、何かがJsonとしてコンテンツを読み込もうとしていたライブラリ(extdirectspring)にbizaareエラーが発生しました。フィルターでボディーを読み取るコードを実行すると、再び機能するようになりました。

私の呼び出し元のコードはこのように見えた:

@Override 
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException 
{ 
    HttpServletRequest properRequest = ((HttpServletRequest)request); 

    String pathInfo = properRequest.getPathInfo(); 
    String target = ""; 
    if(pathInfo == null) 
     pathInfo = ""; 

    if(pathInfo.equals("/router")) 
    { 
     //note this is because servlet requests hate you! 
     //if you read their contents more than once then they throw an exception so we need to do some madness 
     //to make this not the case 
     WrappedRequest wrappedRequest = new WrappedRequest(properRequest); 
     target = ParseExtDirectTargetFrom(wrappedRequest); 
     request = wrappedRequest; 
    } 

    boolean callingSpecialResetMethod = pathInfo.equals("/resetErrorState") || target.equals("resetErrorState"); 
    if(_errorHandler.IsRejectingRequests() && !callingSpecialResetMethod) 
     return; 

    try { 
     filterChain.doFilter(request, response); 
    } 
    catch (Exception exception) { 
     ((HttpServletResponse) response).sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "ERROR"); 
     _errorHandler.NotifyOf(exception); 
    } 
} 

私はParseExtDirectTargetFromの内容は省略さてきたが、それは()getReaderを呼び出します。

私のケースでは、フィルタは他のすべてのリクエストに対して機能していましたが、この場合の奇妙な動作によって、何かが正しくないとわかってしまいました(テスト用の賢明な例外処理動作を実装しています)潜在的に将来のランダムなリクエストを破る価値があります(リクエストが壊れた原因を特定できなかったため)。

また、それは壊れたコードが不可避であることは注目に値します - 私はそれが春から何かかもしれないと仮定が、ServletRequestのはずっと上がる - あなたはHttpServlet

をサブクラス化することにより、ゼロからサーブレットを作っていた場合でも、あなたが得るすべてのthats

私の推薦はこれになります - フィルターの要求本文を読まないでください。あなたはあとで奇妙な問題を引き起こすワームの缶を開くでしょう。

0

ContentCachingRequestWrapperクラス。この問題を解決するHttpServletRequestをラップする