2016-03-07 83 views
0

要仅压缩/缩小大于给定大小限制的WebAPI响应,我必须找出要返回的内容的大小。但是,以下行:在DelegatingHandler中调用LoadIntoBufferAsync时出现死锁

response.Content.LoadIntoBufferAsync() 

似乎在API响应周期后面导致死锁。大小被正确确定,并且处理程序执行正常,但之后该请求将永久挂起。

这是我的DelegatingHandler的SendAsync方法。

protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) 
    { 
     return base.SendAsync(request, cancellationToken).ContinueWith(
      task => 
      { 
       HttpResponseMessage response = task.Result; 

       if (response.RequestMessage != null && response.RequestMessage.Headers.AcceptEncoding != null && response.RequestMessage.Headers.AcceptEncoding.Count > 0) 
       { 
        if (response.Content != null) 
        { 
         response.Content.LoadIntoBufferAsync(); // when I remove this line the request finishes, but then ContentLength = 0 

         if (response.Content.Headers.ContentLength > 4000) 
         { 
          string encodingType = GetEncodingType(response.RequestMessage.Headers.AcceptEncoding); // use deflate if possible 
          if (encodingType == "deflate" || encodingType == "gzip") 
          { 
           response.Content = new CompressedContent(response.Content, encodingType); 
          } 
         } 
        } 
       } 

       return response; 
      }, cancellationToken); 
    } 

我尝试了Wait(..),ConfigureAwait(..)和ContinueWith(..)的不同组合。使用async/await语法,我遇到了同样的问题。

编辑:压缩机SerializeToStreamAsync(负责压缩内容流):

protected override Task SerializeToStreamAsync(Stream stream, TransportContext context) 
    { 
     Stream compressedStream = null; 

     if (m_EncodingType == "gzip") 
     { 
      compressedStream = new GZipStream(stream, CompressionMode.Compress, true); 
     } 
     else if (m_EncodingType == "deflate") 
     { 
      compressedStream = new DeflateStream(stream, CompressionMode.Compress, true); 
     } 

     return m_OriginalContent.CopyToAsync(compressedStream).ContinueWith(task => 
     { 
      if (compressedStream != null) 
      { 
       compressedStream.Dispose(); 
      } 
     }); 
    } 

=> Apperently当这之后称为管道破裂:

response.Content.LoadIntoBufferAsync() 

调用的任一两个人单独工作,所以他们必须是读/写内容流时遇到的问题。

+1

那么,目前的变型,绝对是错误的* *。任何时候当你看到一个名称以'Async'结尾的方法,其中的返回值被忽略时,它应该触发警报响铃。 –

+0

我认为问题在于我在CompressedContent类中使用压缩流重新覆盖了内容,因此内容变得混乱。 – Philipp

回答

0

我正在尝试做同样的事情。看起来您正在等待下面的语句:

await response.Content.LoadIntoBufferAsync();

我发现以上这些与死锁无关。这个问题是当您尝试使用获取内容长度:

response.Content.Headers.ContentLength

,并随后尝试访问标题,例如:

private void AddHeaders() 
    { 
     foreach (var header in content.Headers) 
     { 
      Headers.TryAddWithoutValidation(header.Key, header.Value); 
     } 
     Headers.ContentEncoding.Add(compressor.EncodingType); 
    } 

为了解决这个问题,你需要选择性地添加标题,而不是使用foreach循环。例如:

private void AddHeaders() 
    { 
     //foreach (var header in content.Headers) 
     //{ 
     // Headers.TryAddWithoutValidation(header.Key, header.Value); 
     //} 
     Headers.ContentType = new MediaTypeHeaderValue("application/json"); 
     Headers.ContentEncoding.Add(compressor.EncodingType); 
    } 

有选择地使用压缩的其他选项是使用操作过滤器。你可以找到关于它的下面的帖子详细信息:

http://blog.developers.ba/asp-net-web-api-gzip-compression-actionfilter/

+0

我发现当你使用: response.Content.Headers.ContentLength时会发生死锁,并随后尝试访问Header,例如: –