Question
What causes the IllegalStateException when calling getReader() on a Servlet Request?
java.lang.IllegalStateException: getReader() has already been called for this request
Answer
The `java.lang.IllegalStateException: getReader() has already been called for this request` error occurs when you attempt to read the request body multiple times in a Java Servlet. This happens because the servlet container allows the request body to be read only once using the `getReader()` or `getInputStream()` methods. If you try to read it again, you'll encounter this exception. To fix this issue, you can wrap the `HttpServletRequest` and cache the input stream or reader for multiple accesses.
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
public class MyHttpServletRequestWrapper extends HttpServletRequestWrapper {
private byte[] cachedBody;
public MyHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
super(request);
InputStream is = request.getInputStream();
this.cachedBody = is.readAllBytes();
}
@Override
public BufferedReader getReader() throws UnsupportedEncodingException {
return new BufferedReader(new InputStreamReader(new ByteArrayInputStream(cachedBody), getCharacterEncoding()));
}
@Override
public ServletInputStream getInputStream() throws IOException {
return new CachedBodyServletInputStream(cachedBody);
}
}
class CachedBodyServletInputStream extends ServletInputStream {
private ByteArrayInputStream inputStream;
public CachedBodyServletInputStream(byte[] body) {
this.inputStream = new ByteArrayInputStream(body);
}
@Override
public boolean isFinished() {
return inputStream.available() == 0;
}
@Override
public boolean isReady() {
return true;
}
@Override
public void setReadListener(ReadListener readListener) {
// Not needed for this implementation
}
@Override
public int read() throws IOException {
return inputStream.read();
}
}
Causes
- Calling `getReader()` more than once on the same request object.
- Using filters or servlets that internally read the request body without proper handling.
- Attempting to use both `getReader()` and `getInputStream()` on the same request.
Solutions
- Implement a custom `HttpServletRequestWrapper` that caches the request body.
- Read the request body once and store it in a variable to use for later access.
- Ensure that any filters or servlets do not consume the request body if they intend to pass it along.
Common Mistakes
Mistake: Not wrapping the request properly before reading the body.
Solution: Always use a custom wrapper to cache the request body.
Mistake: Calling both `getReader()` and `getInputStream()` in the same filter or servlet.
Solution: Decide on one method to read the request body and stick with it.
Mistake: Not handling exceptions that may occur while reading the body.
Solution: Add appropriate error handling in your wrapper implementation.
Helpers
- IllegalStateException in Servlets
- getReader() called twice
- HttpServletRequestWrapper example
- Java Servlet error handling
- Servlet filter best practices