2010-12-15 7 views
26

Ich habe diesen Code zu lesen:Wie request.getInputStream() mehrere Male

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

und diese web.xml (Pakete werden verkürzt und die Namen geändert, aber es sieht genauso aus)

<?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> 

Ich möchte das Servlet nach dem Filter aufrufen. Ich hatte gehofft, chain.doFilter(...) könnte den Trick tun, aber ich habe immer diesen Fehler auf der Linie mit 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

Ja, es sollte funktionieren. Funktioniert das Servlet ohne den Filter? – morja

+0

Das Servlet funktioniert ohne den Filter und der Filter ohne 'chain.doFilter()' funktioniert auch – user219882

+0

Putting es außerhalb wenn ..sonst half nicht – user219882

Antwort

7

Sie wahrscheinlich raubend starten die HttpServletRequest mit getReader() in:

String ba = getBaId(getBody(httpRequest)); 

Ihr Servlet versucht zu rufen getInputStream() auf die gleiche Anfrage, die nicht erlaubt ist. Was Sie tun müssen, ist eine ServletRequestWrapper verwenden, um eine Kopie des Hauptteils der Anfrage zu erstellen, so dass Sie es mit mehreren Methoden lesen können. Ich habe nicht die Zeit, um ein komplettes Beispiel zu finden, richtig weiß ... sorry ...

+2

Eine Kopie der Anfrage und die Verwendung von 'getReader()' hilft nicht. Die Ausnahme ist 'getReader() wurde bereits für diese Anfrage aufgerufen –

6

Arbeitscode basierend auf der angenommenen Antwort.

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

input in Servlet-Anforderung kann nur einmal verwendet werden, da es Strom, können Sie sie speichert und dann von einem Byte-Array erhalten, kann dieses Problem zu beheben.

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

}

in Filter:

ServletRequest requestWrapper = new HttpServletRequestWrapper(request); 
2

Das ist für mich gearbeitet. Es implementiert 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(); 
      } 
     }; 
    } 

} 

Dann nutzen Sie in Ihrer Methode:

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

request.getInputStream() darf nur einmal lesen. Um diese Methode mehrmals zu verwenden, müssen wir die benutzerdefinierte Aufgabe für die Klasse HttpServletReqeustWrapper ausführen. Sehen Sie sich meine Beispiel-Wrapper-Klasse an.

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

In meinem Fall, verfolgen ich alle eingehenden Anfragen in das Protokoll. Ich erstellte einen Filter

öffentliche Klasse TracerRequestFilter implementiert Filter { private statische endgültige 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 { 

} 

}

Es funktioniert für mich sowohl Servlet 2.5 und 3.0. Ich sehe alle Anfrageparameter sowohl form-codiert als auch JSON-Body anfordern.

Verwandte Themen