Http Servlet 请求在读取一次后丢失 POST 正文中的参数 [英] Http Servlet request lose params from POST body after read it once

查看:65
本文介绍了Http Servlet 请求在读取一次后丢失 POST 正文中的参数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试访问 Java Servlet 过滤器中的两个 http 请求参数,这里没有什么新鲜事,但惊讶地发现这些参数已经被消耗了!因此,它在过滤器链中不再可用.

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.

这似乎仅在参数出现在 POST 请求正文(例如,表单提交)中时才会发生.

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?

到目前为止,我只找到了这个参考:使用 request.getParameter 的 Servlet 过滤器丢失表单数据.

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

谢谢!

推荐答案

顺便说一句,解决这个问题的另一种方法是不使用过滤器链,而是构建自己的拦截器组件,也许使用切面,它可以操作在解析的请求正文上.它也可能更有效,因为您只需将请求 InputStream 转换为您自己的模型对象一次.

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.

但是,我仍然认为想要多次读取请求正文是合理的,特别是当请求通过过滤器链时.我通常会对某些操作使用过滤器链,这些操作希望保留在 HTTP 层,与服务组件分离.

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.

正如 Will Hartung 所建议的那样,我通过扩展 HttpServletRequestWrapper 来实现这一点,使用请求 InputStream 并基本上缓存字节.

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

该解决方案还允许您通过 getParameterXXX 方法多次读取请求正文,因为底层调用是 getInputStream(),它当然会读取缓存的请求 InputStream.

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.

编辑

对于更新版本的 ServletInputStream 接口.您需要提供更多方法的实现,例如 isReadysetReadListener 等.请参阅此 问题,如下面评论中提供的.

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.

这篇关于Http Servlet 请求在读取一次后丢失 POST 正文中的参数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆