Azure App Service-请求中的GZip压缩

时间:2018-02-20 15:07:27

标签: c# azure xamarin asp.net-web-api azure-mobile-services

使用此代码(没有请求压缩部分),我能够从Azure App Service(具有脱机同步的Xamarin.Froms App)获取gzip压缩内容。但是,当我尝试gzip请求http-content时,我得到了一个" Bad Request"。

有什么想法吗?是否可以使用Azure App Service gzip请求内容?

namespace XXX.XXX.XXX.XXX.XXX
{
    public class HttpGZipClientHandler : System.Net.Http.HttpClientHandler
    {
        long time = 0;
        private long _downloadedBytesFromServer;
        private long _downloadedProcessedBytes;
        private long _intendedUploadedBytesToServer;
        private long _uploadedBytesToServer;
        private long _additionalTimeOverhead = 0;

        public override bool SupportsAutomaticDecompression { get { return true; } }
        public long DownloadedBytesFromServer { get { return _downloadedBytesFromServer; } }
        public long DownloadedProcessedBytes { get { return _downloadedProcessedBytes; } }
        public long IntendedUploadedBytesToServer { get { return _intendedUploadedBytesToServer; } }
        public long UploadedBytesToServer { get { return _uploadedBytesToServer; } }
        public long AdditionalTimeOverhead { get { return _additionalTimeOverhead; } }

        public void ResetStatistics()
        {
            _downloadedBytesFromServer = 0;
            _downloadedProcessedBytes = 0;
            _intendedUploadedBytesToServer = 0;
            _uploadedBytesToServer = 0;
            _additionalTimeOverhead = 0;
        }

        protected override async Task<System.Net.Http.HttpResponseMessage> SendAsync(System.Net.Http.HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
        {

            //Save content headers before compressing
            System.Collections.Generic.Dictionary<string, System.Collections.Generic.IEnumerable<string>> savedContentHeaders = new Dictionary<string, IEnumerable<string>>();
            foreach (System.Collections.Generic.KeyValuePair<string, System.Collections.Generic.IEnumerable<string>> keyValue in request.Content.Headers)
            {
                savedContentHeaders.Add(keyValue.Key, keyValue.Value);
            }

            //Compress request content
            System.Diagnostics.Stopwatch sp1 = new System.Diagnostics.Stopwatch();
            sp1.Start();

            _intendedUploadedBytesToServer += request.Content.Headers.ContentLength.HasValue ? request.Content.Headers.ContentLength.Value : 0;

            await request.Content.LoadIntoBufferAsync().ConfigureAwait(false);
            request.Content = new HttpGZipContent(await request.Content.ReadAsByteArrayAsync().ConfigureAwait(false), System.IO.Compression.CompressionMode.Compress);

            byte[] uploadedBytes = await request.Content.ReadAsByteArrayAsync().ConfigureAwait(false);
            _uploadedBytesToServer += uploadedBytes.Length;

            sp1.Stop();
            _additionalTimeOverhead += sp1.ElapsedMilliseconds;

            //Set headers
            foreach (System.Collections.Generic.KeyValuePair<string, System.Collections.Generic.IEnumerable<string>> keyValue in savedContentHeaders)
            {
                request.Content.Headers.Add(keyValue.Key, keyValue.Value);
            }

            request.Headers.AcceptEncoding.Add(new System.Net.Http.Headers.StringWithQualityHeaderValue("gzip"));
            request.Content.Headers.Add("Content-Encoding", "gzip");

            //Execute request
            System.Net.Http.HttpResponseMessage response = await base.SendAsync(request, cancellationToken).ConfigureAwait(false);
            _downloadedBytesFromServer += response.Content.Headers.ContentLength.HasValue ? response.Content.Headers.ContentLength.Value : 0;

            //Decompress response content
            if (response.Content.Headers.ContentEncoding.Contains("gzip"))
            {
                System.Diagnostics.Stopwatch sp2 = new System.Diagnostics.Stopwatch();
                sp2.Start();

                await response.Content.LoadIntoBufferAsync().ConfigureAwait(false);
                response.Content = new HttpGZipContent(await response.Content.ReadAsByteArrayAsync().ConfigureAwait(false), System.IO.Compression.CompressionMode.Decompress);

                byte[] processedBytes = await response.Content.ReadAsByteArrayAsync().ConfigureAwait(false);
                _downloadedProcessedBytes += processedBytes.Length;

                sp2.Stop();
                _additionalTimeOverhead += sp2.ElapsedMilliseconds;
            }
            else
                _downloadedProcessedBytes += response.Content.Headers.ContentLength.HasValue ? response.Content.Headers.ContentLength.Value : 0;

            return response;
        }
    }

    internal sealed class HttpGZipContent : System.Net.Http.HttpContent
    {
        private readonly byte[] _content;
        private readonly System.IO.Compression.CompressionMode _compressionMode;

        public HttpGZipContent(byte[] content, System.IO.Compression.CompressionMode compressionMode)
        {
            _compressionMode = compressionMode;
            _content = content;
        }

        protected override async System.Threading.Tasks.Task SerializeToStreamAsync(System.IO.Stream stream, System.Net.TransportContext context)
        {
            if (_compressionMode == System.IO.Compression.CompressionMode.Compress)
            {
                using (System.IO.MemoryStream memoryStream = new System.IO.MemoryStream(_content.Length))
                {
                    using (System.IO.Compression.GZipStream zipStream = new System.IO.Compression.GZipStream(memoryStream, System.IO.Compression.CompressionMode.Compress))
                    {
                        zipStream.Write(_content, 0, _content.Length);
                        zipStream.Flush();
                    }

                    byte[] compressed = memoryStream.ToArray();
                    System.IO.MemoryStream copyStream = new System.IO.MemoryStream(compressed);
                    await copyStream.CopyToAsync(stream).ConfigureAwait(false);
                }
            }
            else
            {
                using (System.IO.MemoryStream memoryStream = new System.IO.MemoryStream(_content, 0, _content.Length))
                {
                    using (System.IO.Compression.GZipStream zipStream = new System.IO.Compression.GZipStream(memoryStream, System.IO.Compression.CompressionMode.Decompress))
                    {
                        await zipStream.CopyToAsync(stream).ConfigureAwait(false);
                    }
                }
            }
        }

        protected override bool TryComputeLength(out long length)
        {
            length = _content.Length;
            return true;
        }

        protected override void Dispose(bool disposing)
        {
            base.Dispose(disposing);
        }
    }
}

1 个答案:

答案 0 :(得分:0)

根据我的理解,您需要为移动应用后端实施请求解压缩。如果您使用的是C#后端,则可以按如下方式创建自定义ActionFilterAttribute

public class RequestDeCompressFilter : ActionFilterAttribute
{
    public override async Task OnActionExecutingAsync(HttpActionContext actionContext, CancellationToken cancellationToken)
    {
        var request = actionContext.Request;
        if (request.Content.Headers.ContentEncoding.Contains("GZIP"))
        {
            await request.Content.LoadIntoBufferAsync().ConfigureAwait(false);
            request.Content = new HttpGZipContent(await request.Content.ReadAsByteArrayAsync().ConfigureAwait(false), System.IO.Compression.CompressionMode.Decompress);
        }
       //TODO: compress the response, you could follow http://www.intstrings.com/ramivemula/articles/jumpstart-47-gzipdeflate-compression-in-asp-net-mvc-application/
       await base.OnActionExecutingAsync(actionContext, cancellationToken);
    }

    public override Task OnActionExecutedAsync(HttpActionExecutedContext actionExecutedContext, CancellationToken cancellationToken)
    {   
        //you could also compress the response here
        return base.OnActionExecutedAsync(actionExecutedContext, cancellationToken);
    }
}

然后,按如下方式标记您的行动:

[RequestDeCompressFilter]
public async Task<IHttpActionResult> PostMessage(Message item)

此外,您可以按照HTTP Message Handlers in ASP.NET Web API来实施HTTP消息处理程序。