具有AutomaticDecompression和GetAsync(超时)与GetStringAsync的System.Net.Http.HttpClient(工作

时间:2016-09-01 12:37:42

标签: android xamarin dotnet-httpclient

我有以下代码使用Xamarin和Android设备向REST API发出请求:

public class ApiBase
{
  HttpClient m_HttpClient;

  public ApiBase(string baseAddress, string username, string password)
  {
    if (!baseAddress.EndsWith("/"))
    {
      baseAddress += "/";
    }
    var handler = new HttpClientHandler();
    if (handler.SupportsAutomaticDecompression)
    {
      handler.AutomaticDecompression = DecompressionMethods.GZip;
    }
    m_HttpClient = new HttpClient(handler);
    m_HttpClient.BaseAddress = new Uri(baseAddress);
    var credentialsString = Convert.ToBase64String(Encoding.UTF8.GetBytes(username + ":" + password));
    m_HttpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", credentialsString);
    m_HttpClient.Timeout = new TimeSpan(0, 0, 30);
  }

  protected async Task<XElement> HttpGetAsync(string method)
  {
    try
    {

      HttpResponseMessage response = await m_HttpClient.GetAsync(method);
      if (response.IsSuccessStatusCode)
      {
        // the request was successful, parse the returned string as xml and return the XElement
        var xml = await response.Content.ReadAsAsync<XElement>();
        return xml;
      }
      // the request was not successful -> return null
      else
      {
        return null;
      }
    }
    // some exception occured -> return null
    catch (Exception)
    {
      return null;
    }
  }
}

如果我有这样的话,对HttpGetAsync的第一次和第二次调用都能很好地工作,但是从GetAsync档位的第3次调用,最终因超时而抛出异常。我连续发送这些呼叫,其中没有两个同时运行,因为需要先前呼叫的结果来决定下一个呼叫。 我尝试使用应用程序数据包捕获来查看请求和响应,以查明我是否发送了错误的请求。但看起来最终失败的请求似乎从未被发送过。

通过实验,我发现如果不设置AutomaticDecompression,一切正常。

如果我将HttpGetAsync方法改为:

,它也可以正常工作
protected async Task<XElement> HttpGetAsync(string method)
{
  try
  {
    // send the request
    var response = await m_HttpClient.GetStringAsync(method);
    if (string.IsNullOrEmpty(response))
    {
      return null;
    }
    var xml = XElement.Parse(response);
    return xml;
  }
  // some exception occured -> return null
  catch (Exception)
  {
    return null;
  }
}

所以基本上使用i&#39; m m_HttpClient.GetStringAsync代替m_HttpClient.GetAsync,然后改变它周围的绒毛以使用不同的返回类型。如果我这样做,一切都没有任何问题。

有没有人知道为什么GetAsync无法正常工作(似乎没有发送第3个请求)AutomaticDecompressionGetStringAsync无瑕疵地工作?

1 个答案:

答案 0 :(得分:1)

有关此确切问题的错误报告:
https://bugzilla.xamarin.com/show_bug.cgi?id=21477

该错误标记为已解决已修复,推荐的操作是更新为最新的稳定版本。但还有其他(较新的)bug报告表明同样的事情仍然是开放的,例如: https://bugzilla.xamarin.com/show_bug.cgi?id=34747

通过实现我自己的HttpHandler,我做了一个解决方法:

        public class DecompressionHttpClientHandler : HttpClientHandler
        {
            protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
            {
                request.Headers.AcceptEncoding.Add(new System.Net.Http.Headers.StringWithQualityHeaderValue("gzip"));
                var msg = await base.SendAsync(request, cancellationToken);
                if (msg.Content.Headers.ContentEncoding.Contains("gzip"))
                {
                    var compressedStream = await msg.Content.ReadAsStreamAsync();
                    var uncompresedStream = new System.IO.Compression.GZipStream(compressedStream, System.IO.Compression.CompressionMode.Decompress);
                    msg.Content = new StreamContent(uncompresedStream);
                }
                return msg;
            }
        }

请注意,上面的代码只是一个示例,而不是最终解决方案。例如,请求不会被压缩,所有标题都将从结果中条带化。但是你明白了。