0

要仅对大于给定大小限制的 WebAPI 响应进行压缩/放气,我必须找出要返回的内容的大小。但是以下行:

 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 语法我遇到了同样的问题。

编辑: Compressor 的 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();
            }
        });
    }

=> 显然,当它被调用时,管道被破坏了:

response.Content.LoadIntoBufferAsync()

单独调用两者中的任何一个都可以,因此它们在读取/写入内容流时一定是个问题。

4

1 回答 1

0

我试图做同样的事情。您似乎在以下语句中缺少 await :

等待响应.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/

于 2016-06-09T15:26:07.287 回答