2011-09-06 34 views
14

Quiero agregar el registro a mi Servlet, así que he creado el Filtro que debería mostrar la solicitud y dirigirse al Servlet. Pero, por desgracia no tengo excepción encoutered:java.lang.IllegalStateException: getReader() ya se ha llamado para esta solicitud

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) 

Así que para solucionar este problema que he encontrado la solución con la envoltura, pero no funciona. ¿Qué más puedo usar/cambiar en el código? ¿Algunas ideas?

[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); 
    } 
} 

Respuesta

9

Parece que el marco Restlet ha llamado getRequestEntityStream() en el objeto Request que a su vez llama a getInputStream(), así que llamar getReader() en la solicitud de tiros IllegalStateException . La documentación de la API Servlet para getReader() y getInputStream() dice:

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 

De la documentación parece que no podemos llamar tanto getReader() y getInputStream() en el objeto Request. Le sugiero que use getInputStream() en lugar de getReader() en su envoltorio.

5

El principal problema es que no se puede leer la entrada como flujo binario y flujo de caracteres, ni siquiera si se llama uno en un filtro y el otro en el servlet.

3

Por lo que puedo decir, los servlets están fundamentalmente rotos en este sentido. Puede intentar solucionar este problema como se describe en here, pero eso causa otros problemas misteriosos cuando otras cosas intentan y funcionan con él.

Efectivamente, sugiere clonar la solicitud, leyendo el cuerpo y luego en la clase clonada, anulando los métodos getReader y getInputStream para devolver lo que ya se recuperó.

El código acabé con era la siguiente:

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())); 
    } 
} 

De todos modos esto parecía estar funcionando bien hasta que nos dimos cuenta de que la posibilidad de subir un archivo desde el navegador no estaba funcionando. Atravesé los cambios y descubrí que este era el culpable.

Algunas personas en los comentarios de ese artículo dicen que debe sobrescribir los métodos para hacer con los parámetros, pero no explican cómo hacerlo.

Como resultado, verifiqué si había alguna diferencia en las dos solicitudes. Sin embargo, después de clonar la solicitud, tenía conjuntos idénticos de parámetros (tanto la solicitud original + clonado no tenía ninguno) como un conjunto idéntico de encabezados.

Sin embargo, de alguna manera la solicitud se estaba efectuando y complicando la comprensión de la solicitud más adelante en mi caso provocando un error bizaare en una biblioteca (extdirectspring) donde algo intentaba leer el contenido como Json. Sacar el código que leía el cuerpo en el filtro lo hizo funcionar nuevamente.

Mi código de llamada se veía así:

@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); 
    } 
} 

He omitido el contenido de ParseExtDirectTargetFrom pero getReader llamadas().

En mi caso, el filtro funcionaba para todas las demás solicitudes pero el extraño comportamiento en este caso me hizo darme cuenta de que algo no estaba bien y lo que estaba intentando hacer (implementar un comportamiento de manejo de excepciones sensible para las pruebas) vale la pena potencialmente romper futuras solicitudes aleatorias (ya que no pude entender qué había causado que la solicitud se rompiera).

También vale la pena señalar que el código roto es inevitable - Supuse que podría ser algo de primavera, pero ServletRequest va todo el camino hasta - eso es todo lo que se ponen aún si estuviera haciendo un servlet desde cero subclasificando HttpServlet

Mi recomendación sería esta: no lea el cuerpo de la solicitud en un filtro. Abrirás una lata de lombrices que te causará problemas extraños más adelante.

0

Use ContentCachingRequestWrapper clase. Envuelva HttpServletRequest en este resolverá el problema

Cuestiones relacionadas