Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
332 views
in Technique[技术] by (71.8m points)

java - Http Servlet request lose params from POST body after read it once

I'm trying to accessing two http request parameters in a Java Servlet filter, nothing new here, but was surprised to find that the parameters have already been consumed! Because of this, it is not available anymore in the filter chain.

It seems that this only occurs when the parameters comes in a POST request body (a form submit, for example).

Is there a way to read the parameters and NOT consume them?

So far I've found only this reference: Servlet Filter using request.getParameter loses Form data.

Thanks!

Question&Answers:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

As an aside, an alternative way to solve this problem is to not use the filter chain and instead build your own interceptor component, perhaps using aspects, which can operate on the parsed request body. It will also likely be more efficient as you are only converting the request InputStream into your own model object once.

However, I still think it's reasonable to want to read the request body more than once particularly as the request moves through the filter chain. I would typically use filter chains for certain operations that I want to keep at the HTTP layer, decoupled from the service components.

As suggested by Will Hartung I achieved this by extending HttpServletRequestWrapper, consuming the request InputStream and essentially caching the bytes.

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

Now the request body can be read more than once by wrapping the original request before passing it through the filter chain:

public class MyFilter implements Filter {
  @Override
  public void doFilter(ServletRequest request, ServletResponse response,
        FilterChain chain) throws IOException, ServletException {

    /* wrap the request in order to read the inputstream multiple times */
    MultiReadHttpServletRequest multiReadRequest = new MultiReadHttpServletRequest((HttpServletRequest) request);

    /* here I read the inputstream and do my thing with it; when I pass the
     * wrapped request through the filter chain, the rest of the filters, and
     * request handlers may read the cached inputstream
     */
    doMyThing(multiReadRequest.getInputStream());
    //OR
    anotherUsage(multiReadRequest.getReader());
    chain.doFilter(multiReadRequest, response);
  }
}

This solution will also allow you to read the request body multiple times via the getParameterXXX methods because the underlying call is getInputStream(), which will of course read the cached request InputStream.

Edit

For newer version of ServletInputStream interface. You need to provide implementation of few more methods like isReady, setReadListener etc. Refer this question as provided in comment below.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...