在Tomcat中读取HttpServletRequest的POST请求体并调用getParameter函数

10
我处于这样一种情况:我的应用程序需要检查POST请求的内容/数据/主体/负载,而不改变后续 getParameter 调用的结果。
从 inputStream 中读取主体:
可以使用 request.getInputStreamrequest.getReader 中的 InputStream 读取主体。
读取 POST 参数:
POST 请求通常在请求的主体中包含请求参数。这些可以使用 getParameter 检索。
问题:
第一个 getParameter 调用内部解析 inputStream 并将所有参数插入参数 HashMap。它需要仍然包含解析内容的 inputStream。因此,无法检查内容并仍然具有工作的 getParameter 调用。
建议(但不足够)的解决方案:
创建一个请求包装器,缓存 inputstream 并返回缓存以获取 InputStream。
我已经看到这种解决方案在整个 web 上都被提出过,但它不起作用,因为 getParameter 实际上没有调用 getInputStream,而是引用了 buried 在 request 对象中的原始 inputBuffer。我已经尝试过了,既从 Servlet 内部,也通过使用过滤器。
我能想到的唯一解决方案涉及重新编写 getParameter 以手动解析缓存的 inputstream。但是这感觉像一个坏主意。
是否有人有任何其他有效的替代方案?(这是 Tomcat 5.5)这似乎应该是一个常见的用例;我无法相信它有多么困难。

1
那个问题的答案并不是解决方案。我已经实现了它,但未能解决问题。此后,我阅读了Tomcat源代码,并意识到getParameter并没有调用getInputStream,如我所述,因此不会从流的缓存版本中读取。 请参见http://grepcode.com/file/repo1.maven.org/maven2/tomcat/catalina/5.5.23/org/apache/catalina/connector/RequestFacade.java?av=f#342。我不确定为什么人们似乎认为这是一个解决方案。 - rewolf
方法 getParameter 调用 getInputStream。首先,getParameter 调用 parseParameters,然后 parseParameters 调用 readPostBody,最后 readPostBody 调用 getInputStream - peakmuma
2个回答

1

这只老旧的tomcat,我认为升级到更现代化的版本不是一个选项。

你想要做的事情需要拦截构建包装底层InputStream的具体HttpServletResponse对象。将该InputStream包装成推回输入流(或等效流)是必要的。

Tomcat 5.5太过陈旧,我甚至无法想象如何“正常”地完成这一任务,但也许你可以编写一个使用反射的过滤器,以达到在具体请求对象中交换InputStream对象的目的。


是的。你对反射的想法非常准确。我们也想到了这个。不过我选择采用不同的方法——在过滤器中创建一个请求包装器,支持重放输入流。然后我重写了getParameter*()方法,从通过解析输入流/有效负载创建的映射中读取。 - rewolf

1

根据@caskey的建议,一个可能的解决方案是使用反射来替换inputBuffer为可重放的inputBuffer。但我没有采用这种方法,因为感觉有些不好。

相反,我创建了一个请求包装器,在过滤器中将inputstream读入字节数组,并返回一个新的InputStream,该流在所有getInputStream调用中内部使用ByteArrayInputStream包装该数组。

在将inputstream读取到字节数组后,我通过解析有效负载创建参数映射。我合并了超类的参数映射以支持带有查询参数的GET情况。我重写了所有的getParameter*()方法来使用此参数映射。

我使用apache.axis.utils.IOUtils.readFully轻松地将流读入字节数组。目前,我正在使用javax.servlet.http.HttpUtils.parsePostData将数据解析为参数映射。HttpUtils.parsePostData实际上已经过时了,所以我找到更好的版本后可能会替换它。

但这个方法很有效,耶!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接