ASP.NET Web API GZip compression ActionFilter with 8 lines of code

If you are building high performance applications that consumes WebAPI, you often need to enable GZip / Deflate compression on Web API.

Compression is an easy way to reduce the size of packages and in the same time increase the speed of communication between client and server.

Two common algorithms used to perform this on the Web are GZip and Deflate. These two algorithms are recognized by all web browsers and all GZip and Deflate HTTP responses are automatically decompressed.

On this picture you can see the benefits of GZip compression.

 

compression

Source : Effects of GZip compression

How to implement compression on ASP.NET Web API

There are several ways to do this. One is to do it on IIS level. This way all responses from ASP.NET Web API will be compressed.

Another way is to implement custom delegating handler to do this. I have found several examples of how to do this on the internet but it often requires to write a lot of code.

The third way is to implement your own actionFilter which can be used on method level, controller level or for entire WebAPI.
 

 

Implement ASP.NET Web API GZip compression ActionFilter

For this example with around 8 lines of code I will use very popular library for Compression / Decompression called DotNetZip library .This library can easily be downloaded from NuGet.

Now we implement  Deflate compression ActionFilter.

 public class DeflateCompressionAttribute : ActionFilterAttribute
 {

    public override void OnActionExecuted(HttpActionExecutedContext actContext)
    {
        var content = actContext.Response.Content;
        var bytes = content == null ? null : content.ReadAsByteArrayAsync().Result;
        var zlibbedContent = bytes == null ? new byte[0] : 
        CompressionHelper.DeflateByte(bytes);
        actContext.Response.Content = new ByteArrayContent(zlibbedContent);
        actContext.Response.Content.Headers.Remove("Content-Type");
        actContext.Response.Content.Headers.Add("Content-encoding", "deflate");
        actContext.Response.Content.Headers.Add("Content-Type","application/json");
        base.OnActionExecuted(actContext);
      }
  }

 

We also need a helper class to perform compression.

public class CompressionHelper
{ 
        public static byte[] DeflateByte(byte[] str)
        {
            if (str == null)
            {
                return null;
            }

            using (var output = new MemoryStream())
            {
                using (
                    var compressor = new Ionic.Zlib.DeflateStream(
                    output, Ionic.Zlib.CompressionMode.Compress, 
                    Ionic.Zlib.CompressionLevel.BestSpeed))
                {
                    compressor.Write(str, 0, str.Length);
                }

                return output.ToArray();
            }
        }
}

 

For GZipCompressionAttribute implementation is exactly the same. You only need to call GZipStream instead of DeflateStream in helper method implementation.

If we want to mark some method in controller to be Deflated just put this ActionFilter attribute above method like this :

public class V1Controller : ApiController
{
  
    [DeflateCompression]
    public HttpResponseMessage GetCustomers()
    {

    }

}

 

If you find some better way to perform this please let me know.

 

32 Comments on ASP.NET Web API GZip compression ActionFilter with 8 lines of code

  1. Hiren Thacker
    July 9, 2014 at 11:38 am (10 years ago)

    Hey, This was very helpful.
    One thing I added to your code was that you should be adding all the headers from content to new content since you are resetting the content.

    if (content != null)
    {
    foreach (var httpContentHeader in content.Headers)
    {
    actContext.Response.Content.Headers.Add(httpContentHeader.Key, httpContentHeader.Value);
    }
    }

    Reply
    • Morten
      July 13, 2019 at 2:51 pm (5 years ago)

      Hi Hiren. I know it’s a few years back, however, is your code meant to replace the following 3 lines:

      actContext.Response.Content.Headers.Remove(“Content-Type”);
      actContext.Response.Content.Headers.Add(“Content-encoding”, “deflate”);
      actContext.Response.Content.Headers.Add(“Content-Type”,”application/json”);

      Thanks in advance.
      Morten

      Reply
  2. Radenko Zec
    July 9, 2014 at 11:56 am (10 years ago)

    Hi Hiren. Thanks for comment and code improvement.
    It is logical to maintain all headers from previous content.
    Nice job.

    Reply
    • Radenko Zec
      July 17, 2014 at 6:55 am (10 years ago)

      Hi JD. Thanks for comment.
      Actually approach you are using is quite good.
      Approach described in this article you need to add reference to other project DotNetZip.
      What are the main benefits: Code is simple and small,
      I can control speed of compression and quality of compression (have 10 levels) CompressionLevel.BestSpeed,CompressionLevel.BestCompression…

      DotNetZip is one of the fastest compression libraries (some authors claim it is much faster then Microsoft implementation)

      Approach in your article is also very good. If it works for you well, you should continue use it.

      Reply
  3. Taiseer Joudeh
    July 17, 2014 at 10:28 am (10 years ago)

    Thanks Radenko, very useful with minimum number of LOC 🙂

    Reply
  4. Vedran Mandić
    September 10, 2014 at 2:37 pm (10 years ago)

    Saving the day again! 🙂 A 3 minute implementation with minimal LOC! Hvala!

    Reply
    • Radenko Zec
      September 10, 2014 at 3:57 pm (10 years ago)

      Nema na cemu 😉

      Reply
  5. Siva Saripilli
    September 15, 2014 at 3:04 am (10 years ago)

    Is the output response expected to be zlibbedContent?

    Reply
    • Radenko Zec
      September 15, 2014 at 6:12 am (10 years ago)

      Hi Siva.
      Thanks for comment. Expected output is deflate (zlibbed) content.

      Reply
  6. Roman Chyzh
    September 24, 2014 at 2:03 pm (10 years ago)

    Hi, Radenko. Why you remove Content-Type and then add application/json? What’s reason? If i use both types (xml and json) how can i processed it? Thanks.

    Reply
    • Radenko Zec
      September 25, 2014 at 8:36 pm (10 years ago)

      Hi Roman. Thanks for comment.
      Sorry for late response.
      I think this piece of code you mention is left-over.(I have just copied code for this blog post from my production project)

      There was some need to remove and re-add application/json I think.
      You probably can just remove those 2 lines of code.

      Reply
      • Roman Chyzh
        September 29, 2014 at 12:27 pm (10 years ago)

        @radenkozec:disqus, well, thanks for answer 🙂

        Reply
    • Aung Tun Tint Kyaw
      September 18, 2015 at 6:23 am (9 years ago)

      Above code is awesome thanks. Just one inaccuracy, setting content as new ByteArrayContent will set your content type to text/html. That’s the reason for removing Content-Type header and adding it back in. Only we should cache the incoming content type and set it back instead of just setting it as ‘application/json’

      var contentType = actionExecutedContext.Response.Content.Headers.ContentType.ToString();
      var bytes = content == null ? null : content.ReadAsByteArrayAsync().Result;

      actionExecutedContext.Response.Content = new ByteArrayContent(compressedContent);
      … remove content-type header below because above line sets content-type to text/html
      actionExecutedContext.Response.Content.Headers.Remove(“Content-Type”);

      actionExecutedContext.Response.Content.Headers.Add(“Content-Type”, contentType);

      Reply
  7. Wai Yan
    July 3, 2015 at 9:34 am (9 years ago)

    This is really helpful and the best solution I ever found .

    Reply
  8. Thorium
    December 11, 2015 at 11:40 am (8 years ago)

    Thanks, I made an open source module based on this: https://github.com/Thorium/Owin.Compression But there is deflate also in .NET so you don’t need 3rd party library for that anymore.

    Reply
    • Radenko Zec
      December 11, 2015 at 1:32 pm (8 years ago)

      Thanks for comment. Actually we had deflate in .NET when I wrote this article. There are some other benefits using 3rd party library for this. Read one of the first comments bellow for more info.

      Reply
  9. Kiran
    December 13, 2015 at 7:57 am (8 years ago)

    we can use IIS compression techniques. Do we need this even after IIS compression?

    Reply
    • Radenko Zec
      December 13, 2015 at 10:03 am (8 years ago)

      No. Actually using IIS compression is alternative way to do same thing. However if you need to fine tune your compression such as enable it on only some action methods or controllers you cannot use IIS compression.

      Reply
  10. chaitanya kumar
    December 15, 2015 at 6:14 am (8 years ago)

    I have used the above code as it is

    in

    public class CompressionHelper

    {

    public static byte[] DeflateByte(byte[] str)

    {

    if (str == null)

    {

    return null;

    }

    using (var output = new MemoryStream())

    {

    using (var compressor = new Ionic.Zlib.GZipStream(

    output, Ionic.Zlib.CompressionMode.Compress,

    Ionic.Zlib.CompressionLevel.BestSpeed))

    {

    compressor.Write(str, 0, str.Length);

    }

    return output.ToArray();

    }

    }

    }

    in the line

    using (var compressor = new Ionic.Zlib.GZipStream(

    output, Ionic.Zlib.CompressionMode.Compress,

    Ionic.Zlib.CompressionLevel.BestSpeed))

    after executing this line in composer

    Length = ‘compressor.Length’ threw an exception of type ‘System.NotImplementedException’

    TotalIn = ‘compressor.TotalIn’ threw an exception of type ‘System.NullReferenceException’

    Position = 0x0000000000000000

    Position = 0x0000000000000000

    Reply
  11. chaitanya kumar
    December 15, 2015 at 6:15 am (8 years ago)

    please help me out i am new to the web api and to development

    Reply
  12. chaitanya kumar
    December 15, 2015 at 11:38 am (8 years ago)

    hi i am getting data like this

    Reply
  13. chaitanya kumar
    December 15, 2015 at 11:39 am (8 years ago)

    and even in the fiddle i am not able to see Content-encoding in the header section

    Reply
  14. RJJ
    March 21, 2016 at 12:58 pm (8 years ago)

    Hi,
    Thanks a lot , i was able to compress the response , but facing issues in decompression ,i tried changing the CompressionMode to Decompress , I get the following error :
    Ionic.Zlib.ZlibException: Bad state (invalid block type)

    Reply
  15. Gbenro Selere
    September 29, 2017 at 9:37 am (7 years ago)

    How can this be implement for all methods in a controller?

    Reply
  16. lagvendra Gangwar
    November 17, 2018 at 11:02 am (5 years ago)

    Hi How can we achieve same compression in asp.net core 2.1 web api at action label.

    Reply
  17. StormRide
    July 3, 2019 at 4:13 pm (5 years ago)

    Does compression actually reduce reponse time . I did try your code . I did not see any difference in response times in browser. although the Content-Length reduced by 5 times .

    Reply
  18. mahesh
    July 25, 2019 at 9:11 am (5 years ago)

    Hi,

    I used your above solution in my application, however when API is call it is giving error ” net::ERR_CONTENT_DECODING_FAILED”. Could you please help fix me this error.

    Thanks in Advance!

    Regards,
    Mahesh Kulkarni

    Reply
  19. Basav
    September 25, 2019 at 6:24 am (5 years ago)

    Can someone please provide a comparison on using IIS dynamic content compression vs using action filter for compression?

    Reply

Leave a Reply