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

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

问题描述

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



似乎只有当参数进入POST请求主体时才会出现这种情况(表单提交,对于例如)。



有没有办法读取参数而不消耗它们?



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



谢谢!

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



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



正如 Will Hartung 我通过扩展 HttpServletRequestWrapper 实现了这个目的,消耗了请求 InputStream 并且基本上缓存字节。

 公共类MultiReadHttpServletRequest扩展HttpServletRequestWrapper {
private ByteArrayOutputStream cachedBytes;

public MultiReadHttpServletRequest(HttpServletRequest request){
super(request);
}

@Override
public ServletInputStream getInputStream()抛出IOException {
if(cachedBytes == null)
cacheInputStream();

返回新的CachedServletInputStream();
}

@Override
public BufferedReader getReader()抛出IOException {
返回新的BufferedReader(new InputStreamReader(getInputStream()));
}

private void cacheInputStream()抛出IOException {
/ *缓存输入流以便多次读取它。对于
*方便,我使用apache.commons IOUtils
* /
cachedBytes = new ByteArrayOutputStream();
IOUtils.copy(super.getInputStream(),cachedBytes);
}

/ *一个读取缓存请求体的输入流* /
公共类CachedServletInputStream扩展了ServletInputStream {
private ByteArrayInputStream输入;

public CachedServletInputStream(){
/ *从缓存的请求体创建一个新的输入流* /
input = new ByteArrayInputStream(cachedBytes.toByteArray());
}

@Override
public int read()抛出IOException {
return input.read();
}
}
}

现在请求正文可以是通过在传递过滤器链之前包装原始请求来多次读取:

  public class MyFilter实现过滤器{
@Override
public void doFilter(ServletRequest请求,ServletResponse响应,
FilterChain链)抛出IOException,ServletException {

/ *包装请求以便多次读取输入流* /
MultiReadHttpServletRequest multiReadRequest = new MultiReadHttpServletRequest((HttpServletRequest)request);

/ *这里我读了输入流并用它来做我的事情;当我通过过滤器链传递
*包装请求时,其余的过滤器和
*请求处理程序可以读取缓存的输入流
* /
doMyThing(multiReadRequest.getInputStream ());
// OR
anotherUsage(multiReadRequest.getReader());
chain.doFilter(multiReadRequest,response);
}
}

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


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!

解决方案

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.

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

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