2010-12-15 39 views
26

tengo este código:Como leer request.getInputStream() varias veces

@Override 
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 
     throws IOException, ServletException { 
    logger.info("Filter start..."); 

    HttpServletRequest httpRequest = (HttpServletRequest) request; 
    HttpServletResponse httpResponse = (HttpServletResponse) response; 

    String ba = getBaId(getBody(httpRequest)); 

    if (ba == null) { 
     logger.error("Wrong XML"); 
     httpResponse.setStatus(HttpServletResponse.SC_BAD_REQUEST); 
    } else {  

     if (!clients.containsKey(ba)) { 
      clients.put(ba, 1); 
      logger.info("Client map : init..."); 
     } else { 
      clients.put(ba, clients.get(ba).intValue() + 1); 
      logger.info("Threads for " + ba + " = " + clients.get(ba).toString()); 
     } 

     chain.doFilter(request, response); 
    } 
} 

y esto web.xml (paquetes se acortan y los nombres cambian, pero se ve la misma)

<?xml version="1.0" encoding="ISO-8859-1"?> 
<web-app> 
    <filter> 
    <filter-name>TestFilter</filter-name> 
    <filter-class>pkg.TestFilter</filter-class> 
    </filter> 
    <filter-mapping> 
    <filter-name>TestFilter</filter-name> 
    <url-pattern>/*</url-pattern> 
    </filter-mapping> 

    <context-param> 
    <param-name>contextConfigLocation</param-name> 
    <param-value>WEB-INF/applicationContext.xml</param-value> 
    </context-param> 

    <listener> 
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> 
    </listener> 

    <servlet> 
    <servlet-name>Name</servlet-name> 
    <display-name>Name</display-name> 
    <servlet-class>pkg.Name</servlet-class> 
    <load-on-startup>1</load-on-startup> 
    </servlet> 
    <servlet-mapping> 
    <servlet-name>Name</servlet-name> 
    <url-pattern>/services/*</url-pattern> 
    </servlet-mapping> 
</web-app> 

Deseo invocar el servlet después del filtro. Tenía la esperanza de chain.doFilter(...) podría hacer el truco, pero siempre me sale este error en la línea con chain.doFilter(...):

java.lang.IllegalStateException: getInputStream() can't be called after getReader() 
at com.caucho.server.connection.AbstractHttpRequest.getInputStream(AbstractHttpRequest.java:1933) 
at org.apache.cxf.transport.http.AbstractHTTPDestination.setupMessage(AbstractHTTPDestination.java:249) 
at org.apache.cxf.transport.servlet.ServletDestination.invoke(ServletDestination.java:82) 
at org.apache.cxf.transport.servlet.ServletController.invokeDestination(ServletController.java:283) 
at org.apache.cxf.transport.servlet.ServletController.invoke(ServletController.java:166) 
at org.apache.cxf.transport.servlet.AbstractCXFServlet.invoke(AbstractCXFServlet.java:174) 
at org.apache.cxf.transport.servlet.AbstractCXFServlet.doPost(AbstractCXFServlet.java:152) 
at javax.servlet.http.HttpServlet.service(HttpServlet.java:153) 
at javax.servlet.http.HttpServlet.service(HttpServlet.java:91) 
at com.caucho.server.dispatch.ServletFilterChain.doFilter(ServletFilterChain.java:103) 
at pkg.TestFilter.doFilter(TestFilter.java:102) 
at com.caucho.server.dispatch.FilterFilterChain.doFilter(FilterFilterChain.java:87) 
at com.caucho.server.webapp.WebAppFilterChain.doFilter(WebAppFilterChain.java:187) 
at com.caucho.server.dispatch.ServletInvocation.service(ServletInvocation.java:265) 
at com.caucho.server.http.HttpRequest.handleRequest(HttpRequest.java:273) 
at com.caucho.server.port.TcpConnection.run(TcpConnection.java:682) 
at com.caucho.util.ThreadPool$Item.runTasks(ThreadPool.java:743) 
at com.caucho.util.ThreadPool$Item.run(ThreadPool.java:662) 
at java.lang.Thread.run(Thread.java:619) 
+0

Sí, debería funcionar. ¿Funciona el servlet sin el filtro? – morja

+0

El servlet funciona sin el filtro y el filtro sin 'chain.doFilter()' funciona también – user219882

+0

colocándolo afuera si ...else no ayudó – user219882

Respuesta

7

Es probable que empezar a consumir el HttpServletRequest usando getReader() en:

String ba = getBaId(getBody(httpRequest)); 

Su servlet intenta llamar getInputStream() en la misma solicitud, lo cual no está permitido. Lo que necesita hacer es usar un ServletRequestWrapper para hacer una copia del cuerpo de la solicitud, para que pueda leerla con múltiples métodos. No tengo tiempo para encontrar un ejemplo completo, no lo sé ... lo siento ...

+2

Tener una copia de la solicitud y usar 'getReader()' no ayuda. La excepción será 'getReader() ya se ha llamado para esta solicitud' –

6

Código de trabajo basado en la respuesta aceptada.

public class CustomHttpServletRequestWrapper extends HttpServletRequestWrapper { 

private static final Logger logger = Logger.getLogger(CustomHttpServletRequestWrapper.class); 
private final String body; 

public CustomHttpServletRequestWrapper(HttpServletRequest request) { 
    super(request); 

    StringBuilder stringBuilder = new StringBuilder(); 
    BufferedReader bufferedReader = null; 

    try { 
     InputStream inputStream = request.getInputStream(); 

     if (inputStream != null) { 
      bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); 

      char[] charBuffer = new char[128]; 
      int bytesRead = -1; 

      while ((bytesRead = bufferedReader.read(charBuffer)) > 0) { 
       stringBuilder.append(charBuffer, 0, bytesRead); 
      } 
     } else { 
      stringBuilder.append(""); 
     } 
    } catch (IOException ex) { 
     logger.error("Error reading the request body..."); 
    } finally { 
     if (bufferedReader != null) { 
      try { 
       bufferedReader.close(); 
      } catch (IOException ex) { 
       logger.error("Error closing bufferedReader..."); 
      } 
     } 
    } 

    body = stringBuilder.toString(); 
} 

@Override 
public ServletInputStream getInputStream() throws IOException {   
    final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes()); 

    ServletInputStream inputStream = new ServletInputStream() { 
     public int read() throws IOException { 
      return byteArrayInputStream.read(); 
     } 
    }; 

    return inputStream; 
} 
} 
1

flujoEntrada en la solicitud de servlet sólo puede utilizarse una vez debido a que es corriente, se puede almacenar y luego la recibe de un conjunto de bytes, esto se puede resolver.

public class HttpServletRequestWrapper extends javax.servlet.http.HttpServletRequestWrapper { 

private final byte[] body; 

public HttpServletRequestWrapper(HttpServletRequest request) 
     throws IOException { 
    super(request); 
    body = StreamUtil.readBytes(request.getReader(), "UTF-8"); 
} 

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

@Override 
public ServletInputStream getInputStream() throws IOException { 
    final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body); 
    return new ServletInputStream() { 

     @Override 
     public int read() throws IOException { 
      return byteArrayInputStream.read(); 
     } 

     @Override 
     public boolean isFinished() { 
      return false; 
     } 

     @Override 
     public boolean isReady() { 
      return false; 
     } 

     @Override 
     public void setReadListener(ReadListener arg0) { 
     } 
    }; 
} 

}

de filtro:

ServletRequest requestWrapper = new HttpServletRequestWrapper(request); 
2

Esto funcionó para mí. Implementa getInputStream.

private class MyHttpServletRequestWrapper extends HttpServletRequestWrapper { 

    private byte[] body; 

    public MyHttpServletRequestWrapper(HttpServletRequest request) { 
     super(request); 
     try { 
      body = IOUtils.toByteArray(request.getInputStream()); 
     } catch (IOException ex) { 
      body = new byte[0]; 
     } 
    } 

    @Override 
    public ServletInputStream getInputStream() throws IOException { 
     return new ServletInputStream() { 
      ByteArrayInputStream bais = new ByteArrayInputStream(body); 

      @Override 
      public int read() throws IOException { 
       return bais.read(); 
      } 
     }; 
    } 

} 

Luego se lo utiliza en su método:

//copy body 
servletRequest = new MyHttpServletRequestWrapper(servletRequest); 
0

request.getInputStream() se permite para leer una sola vez. Para utilizar este método muchas veces, debemos realizar una tarea personalizada adicional a la clase HttpServletReqeustWrapper. ver mi clase de contenedor de muestra a continuación.

public class MultiReadHttpServletRequest extends HttpServletRequestWrapper { 
    private ByteArrayOutputStream cachedBytes; 

    public MultiReadHttpServletRequest(HttpServletRequest request) { 
     super(request); 
    } 

    @Override 
    public ServletInputStream getInputStream() throws IOException { 
     if (cachedBytes == null) 
      cacheInputStream(); 

     return new CachedServletInputStream(); 
    } 

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

    private void cacheInputStream() throws IOException { 
     /* 
     * Cache the inputstream in order to read it multiple times. For convenience, I use apache.commons IOUtils 
     */ 
     cachedBytes = new ByteArrayOutputStream(); 
     IOUtils.copy(super.getInputStream(), cachedBytes); 
    } 

    /* An inputstream which reads the cached request body */ 
    public class CachedServletInputStream extends ServletInputStream { 
     private ByteArrayInputStream input; 

     public CachedServletInputStream() { 
      /* create a new input stream from the cached request body */ 
      input = new ByteArrayInputStream(cachedBytes.toByteArray()); 
     } 

     @Override 
     public int read() throws IOException { 
      return input.read(); 
     } 
    } 
} 

En mi caso, rastreo todas las solicitudes entrantes en el registro. Creé un filtro

public class TracerRequestFilter implementa el filtro { private static final Logger LOG = LoggerFactory.getLogger (TracerRequestFilter.class);

@Override 
public void destroy() { 

} 

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

    try { 
     if (LOG.isDebugEnabled()) { 
      final MultiReadHttpServletRequest wrappedRequest = new MultiReadHttpServletRequest(req); 
      // debug payload info 
      logPayLoad(wrappedRequest); 
      chain.doFilter(wrappedRequest, response); 
     } else { 
      chain.doFilter(request, response); 
     } 
    } finally { 
     LOG.info("end-of-process"); 
    } 
} 

private String getRemoteAddress(HttpServletRequest req) { 
    String ipAddress = req.getHeader("X-FORWARDED-FOR"); 
    if (ipAddress == null) { 
     ipAddress = req.getRemoteAddr(); 
    } 
    return ipAddress; 
} 

private void logPayLoad(HttpServletRequest request) { 
    final StringBuilder params = new StringBuilder(); 
    final String method = request.getMethod().toUpperCase(); 
    final String ipAddress = getRemoteAddress(request); 
    final String userAgent = request.getHeader("User-Agent"); 
    LOG.debug(String.format("============debug request==========")); 
    LOG.debug(String.format("Access from ip:%s;ua:%s", ipAddress, userAgent)); 
    LOG.debug(String.format("Method : %s requestUri %s", method, request.getRequestURI())); 
    params.append("Query Params:").append(System.lineSeparator()); 
    Enumeration<String> parameterNames = request.getParameterNames(); 

    for (; parameterNames.hasMoreElements();) { 
     String paramName = parameterNames.nextElement(); 
     String paramValue = request.getParameter(paramName); 
     if ("password".equalsIgnoreCase(paramName) || "pwd".equalsIgnoreCase(paramName)) { 
      paramValue = "*****"; 
     } 
     params.append("---->").append(paramName).append(": ").append(paramValue).append(System.lineSeparator()); 
    } 
    LOG.debug(params.toString()); 
    /** request body */ 

    if ("POST".equals(method) || "PUT".equals(method)) { 
     try { 
      LOG.debug(IOUtils.toString(request.getInputStream())); 
     } catch (IOException e) { 
      LOG.error(e.getMessage(), e); 
     } 
    } 
    LOG.debug(String.format("============End-debug-request==========")); 
} 

@Override 
public void init(FilterConfig arg0) throws ServletException { 

} 

}

Funciona para mí, tanto Servlet 2.5 y 3.0. Veo todos los parámetros de solicitud codificados en forma y solicito json body.