【问题标题】:HttpURLConnection getInputStream() sometimes contains response headersHttpURLConnection getInputStream() 有时包含响应标头
【发布时间】:2011-09-05 13:49:12
【问题描述】:

我的 Android 应用使用 HttpURLConnection 对象从我们的服务器请求信息。服务器返回一些文本——根据请求的不同,可能是少量(约 50 个字符)或很多(12 MB)。 mime 类型是 text/plain。

在我最初的实现中,我很失望地发现 getInputStream() 正在从服务器返回整个 HTTP 响应——包括标头——尽管文档说它只包含响应主体。哦,好吧...我实现了一个小状态机来读取和存储标头,并在我的代码中提供接口以允许调用者访问标头字段。从 AsyncTask 调用它以使其脱离 UI 线程并获得简单的进度跟踪方法。一切都很好。

我最近添加了另一个从服务器请求信息的功能,所以我使用了相同的“http 连接包装器”对象,它希望在正文流中看到标头。这一次,使用 Thread 对象将下载从 UI 线程中推送出去很方便。 (我不需要进度跟踪,讽刺的是,Thread 比“便利对象”AsyncTask 更容易实现。)有趣的是,当从 Thread 对象调用时,我没有看到 getInputStream() 流中的标头。

唯一的区别是,在一种情况下,我是从派生自 AsyncTask 的对象内部发出请求,而在另一种情况下,我是从派生自 Thread 的对象内部发出请求。

当我从 AsyncTask 中使用 HttpUrlConnection 时,我会在输入流中看到标头。当我从线程中使用 HttpUrlConnection 时,我只看到响应正文。根据文档,后者是“正确”的行为。

我尝试过切换方法。也就是说,我在 Thread 而不是 AsyncTask 中提出了我的第一个请求。如果我这样做,我看不到标题。当我在 AsyncTask 而不是 Thread 中发出第二个请求时,我看到输入流中的标头只需要正文。所以这个问题似乎与我是在 AsyncTask 中还是在 Thread 中有关。

显然,我可以选择一种或另一种方法来让我的 HTTP 活动脱离 UI 线程,但我想了解发生了什么,这样我就可以让它的行为相同,而不管它是如何被调用的。而且我喜欢使用 AsyncTask,因为它具有简单的内置进度跟踪机制,但这种方法无法正常工作。

想法?有什么建议吗?

更多信息

我已经将我的 HttpURLConnection 访问封装在一个看起来像这样的小函数中(实际代码有一个委托,它将接收到的字符串发送到该委托,但我已经对其进行了简化以进行测试):

/**
 * Reads the data at the URL.
 * @param urlString The URL to read
 */
public void Get(
    String urlString)
    {
    URL url;
    BufferedInputStream in;
    try
        {
        url = new URL(urlString);
        conn = (HttpURLConnection) url.openConnection();
        conn.setRequestMethod("GET");
        conn.setRequestProperty("Accept", "text/*");
        in = new BufferedInputStream(conn.getInputStream(), 8192);

        byte [] buffer = new byte[8192];
        StringBuilder stringBuffer = new StringBuilder(16384);

        int read;
        while ((read = in.read(buffer)) != -1)
            {
            String tempstr = new String(buffer, 0, read, "ISO-8859-1");
            System.out.println(tempstr);
            }
        return true;
        }
    catch (IOException e)
        {
        return false;
        }

我从两个地方调用这个函数。为了测试,我有两个地方从我们的服务器请求相同的页面。首先是在扩展 AsyncTask 的对象中。在 doInBackground() 中,我实例化包含 Get() 的对象,然后调用 Get()。其次是在扩展 Thread 的对象中,我在其中实例化包含 Get() 的对象,然后在 run() 中调用 Get()。在这两种情况下,我都请求同一个页面。

结果是当我从我的 AsyncTask 调用时,我看到了所有的响应标头。我实际看到的是原始 HTTP——包括传输编码被“分块”时的块长度。当我从我的线程调用时,我得到的只是 HTML 的正文,这是我所期望的。

为了测试,我在服务器上编写了一个返回原始请求的脚本。这是我得到的(注意“awebsite”不是我服务器的名称):

在线程中运行时:

Connection: Keep-Alive
Accept: text/*
Host: www.awebsite.com
User-Agent: Java0

在 AsyncTask 中运行时:

[CRLF]
HTTP/1.1 200 OK
Date: Tue, 31 May 2011 23:29:20 GMT
Server: Microsoft-IIS/6.0
X-Powered-By: ASP.NET
Content-Type: text/html
Set-Cookie: ASPSESSIONIDCQSTTQAQ=OHBIFGJCPEAAHGNHICPKOKFO; path=/
Cache-control: private
Transfer-Encoding: chunked
[CRLF]
53
Connection: Keep-Alive
Accept: text/*
Host: www.awebsite.com
User-Agent: Java0
[CRLF]
0
[CRLF]

请注意,这两种情况似乎都发送相同的请求。但是,在我从 AsyncTask 调用 Get() 的情况下,某些东西会诱使 HttpURLConnection 将响应标头与正文一起提供给我。

更令人困惑的是,访问 www.google.com 可以按预期工作——我从来没有看到标题与正文混合在一起。所以这似乎表明服务器端有问题(这让我想知道服务器如何知道失败)或者服务器如何响应在 AsyncTask 中运行时混淆了 HttpURLConnection。

【问题讨论】:

  • 我也是这样。我从 Thread 发送请求,有时所有标头都在 getInputStream() 中。也在寻找解决方法。
  • @Chirag,你能告诉我为什么要使用标题。我必须从 android 验证一些凭据我在 xammp 上使用 php。我该怎么做。因为我不知道如何用 headers 编写 php 代码

标签: java android android-asynctask httpurlconnection


【解决方案1】:
  1. 检查 conn.getResponseCode()。如果为 -1,则不应尝试读取流。
  2. 如果您使用的是 2.1 或更低版本,请执行以下操作:System.setProperty("http.keepAlive", "false");
    更多细节在这里HttpURLConnection.getResponseCode() returns -1 on second invocation。

【讨论】:

  • 你能告诉我为什么要使用标题。我必须从 android 验证一些凭据我在 xammp 上使用 php。我该怎么做。因为我不知道如何用 headers 编写 php 代码
【解决方案2】:

我预计您的服务器端生成存在错误:可能有一个响应标头具有几个嵌入的换行符,并且 http 解析器将其作为标头的结尾。

【讨论】:

  • 服务器如何知道我是从 Thread 还是 AsyncTask 请求页面?在任何一种情况下,数据都是相同的(好的、坏的、格式错误的等等)。
  • 我认为 Femi 是对的。您确定在这两种情况下都向服务器请求相同的东西吗?我从来没有遇到过响应包含您所描述的标题的问题。尝试从公共领域请求一些东西,看看你是否也从他们那里得到了标题(使用微软、谷歌、雅虎、苹果......全部尝试:))
  • 好吧,服务器不能知道你是在线程还是异步任务中,所以你必须要么请求不同的东西,要么错误地使用了流水线或保活之类的东西。粘贴您正在使用的 2 块代码。
  • 更多信息:问题似乎与我们的服务器无关,但这并不能解释任何事情。如果我请求 www.google.com,我永远不会看到标题。但是,如果我在我们的服务器上请求一个页面,如果我从 AsyncTask 请求它,我会在正文流中获得标头,但如果我从线程请求它,则只有正文内容。无论在哪个线程中运行,实际执行请求和读取响应的代码都是相同的。
  • 所以您的服务器端代码写入了错误的标头:如果您有权访问 wget 并且无需身份验证即可连接到 URL,您可以执行 wget -S -O - "{url}" 并查看标头位于无法正常工作的请求上。
猜你喜欢
  • 2023-03-16
  • 2016-12-14
  • 2011-06-15
  • 1970-01-01
  • 2018-01-13
  • 2019-08-01
  • 2022-06-25
  • 1970-01-01
相关资源
最近更新 更多