servlet 中读取请求正文的问题

Posted

技术标签:

【中文标题】servlet 中读取请求正文的问题【英文标题】:Problem reading request body in servlet 【发布时间】:2011-03-26 06:15:12 【问题描述】:

我正在编写作为测试/验证一部分的 HTTP 代理 系统。代理过滤来自客户端设备的所有请求 并将它们引导到各种被测系统。

代理被实现为一个 servlet,每个请求都在其中被转发 到目标系统,它同时处理 GET 和 POST。有时 目标系统的响应被改变以适应各种测试 条件,但这不是问题的一部分。

转发请求时,所有标头都被复制,除了那些 这是实际 HTTP 传输的一部分,例如 Content-Length 和 连接头。

如果请求是 HTTP POST,那么请求的实体主体是 也转发,这里有时不起作用。

从servlet请求中读取实体主体的代码如下:

URL url = new URL(targetURL);
HttpURLConnection conn  = (HttpURLConnection)url.openConnection();
String method = request.getMethod();

java.util.Enumeration headers = request.getHeaderNames();
while(headers.hasMoreElements()) 

    String headerName = (String)headers.nextElement();
    String headerValue = request.getHeader(headerName);

    if (...)  // do various adaptive stuff based on header 

    

    conn.setRequestProperty(headerName, headerValue);

//这里是失败的部分

char postBody[] = new char[1024];
int len;

if(method.equals("POST")) 
    logger.debug("guiProxy, handle post, read request body");
    conn.setDoOutput(true);

    BufferedReader br = request.getReader();
    BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(conn.getOutputStream()));

    do 
        logger.debug("Read request into buffer of size: " + postBody.length);

        len = br.read(postBody, 0, postBody.length);
        logger.debug("guiProxy, send request body, got " + len + " bytes from request");

        if(len != -1) 
            bw.write(postBody, 0, len);
        
     while(len != -1);
    bw.close();

所以发生的事情是第一次收到 POST,-1 从请求阅读器读取字符,wireshark 跟踪显示 包含 URL 编码的 post 参数的实体主体存在 它在一个 TCP 段中,因此没有网络相关 差异。

第二次,br.read成功返回了232字节 POST 请求实体正文和每个即将到来的请求也可以正常工作。

第一个和即将到来的 POST 请求之间的唯一区别是 在第一个中,没有 cookie,但在第二个中, 存在一个映射到 JSESSION 的 cookie。

这可能是实体主体不可用的副作用,因为 servlet 容器中的请求处理已经读取 POST 参数,但为什么它对即将到来的请求起作用。

我相信解决方案当然是忽略实体主体 包含 URL 编码数据的 POST 请求并获取所有参数 从 servlet 请求而不是使用 getParameter 并重新插入它们 int 传出请求。

尽管这很棘手,因为 POST 请求可能包含 GET 参数,现在不在我们的应用程序中,而是在实现它 正确的是一些工作。

所以我的问题基本上是:为什么读者从 request.getReader() 读取时返回 -1 并且实体主体是 出现在请求中,如果实体主体不可用于 读取,那么 getReader 应该抛出一个非法状态异常。一世 也尝试过使用 getInputStream() 与 InputStream 相同 结果。

所有这些都在 apache-tomcat-6.0.18 上进行了测试。

【问题讨论】:

【参考方案1】:

所以我的问题基本上是:为什么 request.getReader() 的阅读器在阅读时返回 -1。

当没有正文或已已经被读取时,它将返回 -1。你不能读两遍。确保请求/响应链中之前没有任何内容读取过它。

并且请求中存在实体主体,如果实体主体不可读取,则 getReader 应抛出非法状态异常。

它只会在你之前已经在请求上调用过getInputStream() 时抛出,而不是在它不可用时抛出。

我也尝试过使用 getInputStream() 的 InputStream,结果相同。

毕竟,我更喜欢流式传输字节而不是字符,因为这样你就不需要考虑字符编码(你现在还没有这样做,当你得到这一切时,这可能会导致未来的问题工作)。

【讨论】:

似乎 cookie 的存在阻止了 servlet 容器读取 POST 正文。这就解释了为什么以下发布请求有效,因为设置 cookie 后它就有效。现在对我来说有点尴尬的解决方案是手动将所有 POST 参数传输到代理的请求实体主体,但它现在可以工作。我相信构建代理的正确方法是使用 servlet 过滤器,然后可以控制整个请求。【参考方案2】:

似乎,那动人

BufferedReader br = request.getReader()

在所有操作之前,读取请求(如 request.getHeader() )对我来说效果很好。

【讨论】:

以上是关于servlet 中读取请求正文的问题的主要内容,如果未能解决你的问题,请参考以下文章

AEM 中的 Servlet,POST 请求中的正文丢失

如何使用保留请求正文和响应正文的 servlet 过滤器记录请求和响应?

当一个请求由一个 servlet 处理时,是整个请求头/正文/等。已经加载了吗?

如何在从 servlet 过滤器执行 HTTP 重定向时保留请求正文

Servlet 学习

从 servlet 读取 POST 请求参数