Recently I came across a requirement in which I have to read HttpServletRequest body twice and then pass the request again to the filter chain for the normal application flow. Then I created the below given Java class which can be used inside a servlet filter to intercept the request, read request body content and then pass the request again to the servlet filter chain for further processing.
We must be aware, by default, the HTTP request body can be read only once. If we read the body in a filter, the target servlet will not be able to re-read it and this will also cause IllegalStateException
.
1. Custom HttpServletRequestWrapper
This is the source code of the custom implementation of HttpServletRequestWrapper class. Please note that I am using Servlets 2.5 specification here because I had to work on some legacy applications. Feel free to change the implementation as per the latest servlet specification.
Overall, the wrapper class retrieves the request body and stores in the instance variable body. We can read the body attribute as many times, as we want without getting any errors.
This class can be seen as an example of reading InputStream twice, but it is not.
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
public class RequestWrapper extends HttpServletRequestWrapper {
private final String body;
public RequestWrapper(HttpServletRequest request) throws IOException
{
//So that other request method behave just like before
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) {
throw ex;
} finally {
if (bufferedReader != null) {
try {
bufferedReader.close();
} catch (IOException ex) {
throw ex;
}
}
}
//Store request pody content in 'body' variable
body = stringBuilder.toString();
}
@Override
public ServletInputStream getInputStream() throws IOException {
final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes());
ServletInputStream servletInputStream = new ServletInputStream() {
public int read() throws IOException {
return byteArrayInputStream.read();
}
};
return servletInputStream;
}
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(this.getInputStream()));
}
//Use this method to read the request body N times
public String getBody() {
return this.body;
}
}
2. HttpServletRequestWrapper Usage
Use the wrapper class to modify request parameters in the servlet filter. It will help to servlet read the request body twice.
To use this class, we must first add a servlet filter filter mapping in web.xml
. We will use the wrapper class inside this filter.
<filter>
<filter-name>cacheFilter</filter-name>
<filter-class>com.howtodoinjava.filter.RESTCacheFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>cacheFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Above filter mapping will invoke the filter at all requests because we mapped it to wildcard /*
.
3. Reading Request body Multiple Times
In the servlet filter, we can read the HTTP request body N number of times and then pass it to the filter chain and it will work just fine.
It’s a small utility class and may not be needed in most cases. But when it’s needed – you will know it.
public class CacheFilter implements Filter {
private static final Logger LOGGER = LoggerFactory.getLogger(CacheFilter.class);
private static final String INDEX_DIR = "c:/temp/cache";
private FilterConfig filterConfig = null;
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
request = new RequestWrapper((HttpServletRequest) request);
//Read request.getBody() as many time you need
chain.doFilter(request, response);
}
public void init(FilterConfig filterConfiguration) throws ServletException {
this.filterConfig = filterConfiguration;
}
public void destroy() {
this.filterConfig = null;
}
}
4. Conclusion
Using above given HttpServletRequestWrapper
, you can read the HTTP request body and then the servlet can still read it later. Essentially, request body content is cached inside the wrapper object so it can be N number of times in the complete request lifecycle.
Happy Learning !!
Where does newly created BufferedReader in below method gets closed to have proper resource maintenance?
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(this.getInputStream()));
}
It is used by
HttpServletRequestWrapper
and inside it is closed.did you find any solution for multipart requests. I am also facing same issue.
Hi,
I used similar code for creating RequestWraper for our application to implement Req encryption on client side and decryption on server side. But i facing the issue when i upload an image or zip files they are getting corrupted.
If i stop using RequestWrapper which is newly introduced, Upload and download files are working fine.
I feel we need to do something here when request is “multipart”, Please help me what is the fix for this issue.
Thanks in advance.
In my case, I’m working on legacy code and need to make a patch. The problem: an ancient application sends bad HTTP POST requests. I need to always read the http POST data and update it with the correct post and sent back to REST endpoint. If I read data once, then I am not able to read it again. Can you please guide me on this ?
I have an error in the request = new RequestWrapper (HttpServletRequest) request
error is RequestWrapper (org.apache.http.HttpRequest) in RequestWrapper can not be applied to javax.servlet.http.HttpServletRequest
knowing that for the RequestWrapper I used the following import: org.apache.http.impl.client.RequestWrapper
There’s quite a few drawbacks to this if used with Tomcat 8.
Once you get the InputStream from the original request, then you can’t use getParameter(), unless you parse them on your own.
If you first invoke getParameter to try and get them parsed, and then call getInputStream on the original request, you’ll get a stream that has already reached its end, and you can’t read it again.
I was hoping to use this to re-read bodies and send them as attachments when my error filter catches something. It will take a lot more code to get it to parse the bodies for parameters.
Thank you for ur solution. it works well!!
Hi I trying same code on Web Sphere application server but it is not working. Once the request is read it when I chain the request values goes null there.
Let me know if we can use this modification:
I have 2 filters, and I used your code in one of them, eactly like you did in your example. The first time the filter is called, I read body without problem, but when call chain.doFilter the wrapper is called again, and this time (second time) the line “while …” throw an exception because the stream is closed. I have commented the line where bufferedreader is closed and everything works fine. So, I’d like to know why the wrapper was called twice? And how to close the bufferedreader without close inputstream who have been passed in constructor?