Search code examples
c#asp.net-web-apihttp-compression

Compress HTTP GET Response


I am currently working on migrating few of my MVC3 Controllers to MVC4 Api Controllers. I have implemented Compression mechanism for MVC3 controller Get Method Responses by inherting ActionFilterAttribute and overriding OnActionExecutiong method. After some Research I found that I need to use ActionFilterMethod from System.Web.HttpFilters. It would be great if somebody can share piece of sample code to get me started for this compressing HTTP response using GZip


Solution

  • The easiest is to enable compression directly at IIS level.

    If you want to do it at the application level you could write a custom delegating message handler as shown in the following post:

    public class CompressHandler : DelegatingHandler
    {
        protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            return base.SendAsync(request, cancellationToken).ContinueWith<HttpResponseMessage>((responseToCompleteTask) =>
            {
                HttpResponseMessage response = responseToCompleteTask.Result;
    
                if (response.RequestMessage.Headers.AcceptEncoding != null)
                {
                    string encodingType = response.RequestMessage.Headers.AcceptEncoding.First().Value;
    
                    response.Content = new CompressedContent(response.Content, encodingType);
                }
    
                return response;
            },
            TaskContinuationOptions.OnlyOnRanToCompletion);
        }
    }
    
    public class CompressedContent : HttpContent
    {
        private HttpContent originalContent;
        private string encodingType;
    
        public CompressedContent(HttpContent content, string encodingType)
        {
            if (content == null)
            {
                throw new ArgumentNullException("content");
            }
    
            if (encodingType == null)
            {
                throw new ArgumentNullException("encodingType");
            }
    
            originalContent = content;
            this.encodingType = encodingType.ToLowerInvariant();
    
            if (this.encodingType != "gzip" && this.encodingType != "deflate")
            {
                throw new InvalidOperationException(string.Format("Encoding '{0}' is not supported. Only supports gzip or deflate encoding.", this.encodingType));
            }
    
            // copy the headers from the original content
            foreach (KeyValuePair<string, IEnumerable<string>> header in originalContent.Headers)
            {
                this.Headers.AddWithoutValidation(header.Key, header.Value);
            }
    
            this.Headers.ContentEncoding.Add(encodingType);
        }
    
        protected override bool TryComputeLength(out long length)
        {
            length = -1;
    
            return false;
        }
    
        protected override Task SerializeToStreamAsync(Stream stream, TransportContext context)
        {
            Stream compressedStream = null;
    
            if (encodingType == "gzip")
            {
                compressedStream = new GZipStream(stream, CompressionMode.Compress, leaveOpen: true);
            }
            else if (encodingType == "deflate")
            {
                compressedStream = new DeflateStream(stream, CompressionMode.Compress, leaveOpen: true);
            }
    
            return originalContent.CopyToAsync(compressedStream).ContinueWith(tsk =>
            {
                if (compressedStream != null)
                {
                    compressedStream.Dispose();
                }
            });
        }
    }
    

    All that's left now is to register the handler in Application_Start:

    GlobalConfiguration.Configuration.MessageHandlers.Add(new CompressHandler());