How to read request.getInputStream () several times

I have this code:

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

and this web.xml (the packages are shortened and the names are changed, but they look the same)

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

I want to call Servlet after the filter. I was hoping chain.doFilter(...) could do the trick, but I always get this error on the line using 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) 
+34
java java-ee servlets servlet-filters
Dec 15 '10 at 10:51
source share
6 answers

You will probably start consuming HttpServletRequest using getReader() in:

 String ba = getBaId(getBody(httpRequest)); 

Your servlet is trying to call getInputStream() on the same request, which is not allowed. What you need to do is use ServletRequestWrapper to make a copy of the request body so you can read it in several ways. I donโ€™t have time to find a complete example that knows correctly ... sorry ...

+7
Dec 15 '10 at 13:13
source share

Work code based on the accepted answer.

 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; } } 
+11
Mar 21 '14 at 8:32
source share

It worked for me. It implements 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(); } }; } } 

Then you use in your method:

 //copy body servletRequest = new MyHttpServletRequestWrapper(servletRequest); 
+8
Oct 24 '16 at 20:51
source share

For servlet 3.1

 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 DelegatingServletInputStream(new ByteArrayInputStream(body)); } } public class DelegatingServletInputStream extends ServletInputStream { private final InputStream sourceStream; private boolean finished = false; /** * Create a DelegatingServletInputStream for the given source stream. * * @param sourceStream the source stream (never {@code null}) */ public DelegatingServletInputStream(InputStream sourceStream) { this.sourceStream = sourceStream; } /** * Return the underlying source stream (never {@code null}). */ public final InputStream getSourceStream() { return this.sourceStream; } @Override public int read() throws IOException { int data = this.sourceStream.read(); if (data == -1) { this.finished = true; } return data; } @Override public int available() throws IOException { return this.sourceStream.available(); } @Override public void close() throws IOException { super.close(); this.sourceStream.close(); } @Override public boolean isFinished() { return this.finished; } @Override public boolean isReady() { return true; } @Override public void setReadListener(ReadListener readListener) { throw new UnsupportedOperationException(); } } 
+6
May 29 '18 at 10:13
source share

An inputStream in a servlet request can be used only once, because it is a stream, you can save it, and then get it from a byte array, this can be resolved.

 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 the filter:

 ServletRequest requestWrapper = new HttpServletRequestWrapper(request); 
+1
Aug 17 '16 at 6:58
source share

request.getInputStream () is allowed to read only once. To use this method many times, we need to perform an additional custom task for the HttpServletReqeustWrapper class. see my shell class sample below.

 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 my case, I keep track of all incoming requests to the log. I created a filter

the public class TracerRequestFilter implements Filter {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 { } } 

Both servlet 2.5 and 3.0 work for me. I see all the request parameters both in the encoding of the form and in the body of the json request.

+1
Oct 19 '17 at 7:00
source share



All Articles