java - Http Servlet 请求在读取一次后会丢失 POST 正文中的参数

标签 java servlets parameters httprequest servlet-filters

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

似乎只有当参数出现在 POST 请求正文(例如表单提交)中时才会发生这种情况。

有没有办法读取参数而不使用它们?

到目前为止我只找到了这个引用:Servlet Filter using request.getParameter loses Form data .

谢谢!

最佳答案

顺便说一句,解决此问题的另一种方法是不使用过滤器链,而是构建自己的拦截器组件,也许使用方面,它可以对解析的请求主体进行操作。它也可能会更高效,因为您只需将请求 InputStream 转换为您自己的模型对象一次。

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

根据Will Hartung的建议我通过扩展 HttpServletRequestWrapper、使用请求 InputStream 并本质上缓存字节来实现这一点。

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 input stream which reads the cached request body */
  private static class CachedServletInputStream extends     ServletInputStream {

    private final ByteArrayInputStream buffer;

    public CachedServletInputStream(byte[] contents) {
      this.buffer = new ByteArrayInputStream(contents);
    }

    @Override
    public int read() {
      return buffer.read();
    }

    @Override
    public boolean isFinished() {
      return buffer.available() == 0;
    }

    @Override
    public boolean isReady() {
      return true;
    }

    @Override
    public void setReadListener(ReadListener listener) {
  throw new RuntimeException("Not implemented");
    }
  }
}

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

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

编辑

适用于较新版本的 ServletInputStream 接口(interface)。您需要提供更多方法的实现,例如 isReadysetReadListener 等。请参阅此 question正如下面的评论中所提供的。

关于java - Http Servlet 请求在读取一次后会丢失 POST 正文中的参数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60023548/

相关文章:

java - 运行 servlet 显示错误

function - 以下警告是什么意思: 'side-effecting nullary methods are discouraged' ?

java - JUnit @Rule 将参数传递给测试

java - 如何强制 Jersey 接受查询参数中的 '>' 字符

java - 带迭代的 While 循环

java - 关于servlet多线程环境的问题

java - 最好的 Java 快速排序?

java - 无法在eclipse中添加Servlet

java - 为什么当我将 jar 文件复制到 Android Studio 中的 lib 文件夹时,它一直说 "Refactoring cannot be performed"?

java - OCPJP 考试中是否应该存在棘手的问题?