Compressing Web API Response - Part Two

In this article, you will learn how to compress Web API response by creating an Action Filter.

In one of my articles, I have discussed how to compress Web API Response, using DotNet Zip. You can find the link below:
Compressing Web API Response has two major advantages:
  1. Data size is reduced.
  2. Response time is optimized (increasing the speed of the communication between the Client and the Server).
In this article, I will show how to compress the Web API response to reduce the size of the data and increase the speed of the communication between the Server and the Client.

First of all, create a Web API which returns some data in JSON format. I have created my API, as follows: 
  1. [RoutePrefix("api/Home")]  
  2.     
  3.    public class HomeController : ApiController  
  4.    {  
  5.        [Route("GetData")]  
  6.         
  7.         
  8.        public async Task<IHttpActionResult> getData()  
  9.        {  
  10.            Stopwatch sw = new Stopwatch();  
  11.            sw.Start();  
  12.            Dictionary<objectobject> dict = new Dictionary<objectobject>();  
  13.            List<Employee> li = new List<Employee>();  
  14.            li.Add(new Employee { id = 2, Name = "Debendra", Id = "A123", Email = "Debendra256@gmail.com" });  
  15.            li.Add(new Employee { id = 3, Name = "Sumit", Id = "A124", Email = "Sumit@gmail.com" });  
  16.            li.Add(new Employee { id = 4, Name = "Jayant", Id = "A125", Email = "jsyant@gmail.com" });  
  17.            li.Add(new Employee { id = 5, Name = "Kumar", Id = "A126", Email = "KR@gmail.com" });  
  18.   
  19.            sw.Stop();  
  20.   
  21.            dict.Add("Details", li);  
  22.            dict.Add("Time", sw.Elapsed);  
  23.   
  24.            return Ok(dict);  
  25.   
  26.        }    
  27.    }  
Now, open the Postman and test the response of this API, as shown in the following screen:
 
postman

Now, check the actual size of the Response.

responce
Now, I will check the API by compressing the result.

For compressing, I will create a custom Action Filter, add new class, and rename it as "CompressFilter.cs". Now, I will inherit this class from ActionFilterAttribute and write the following code:
  1. using System.Collections.Generic;  
  2. using System.Linq;  
  3. using System.Web;  
  4. using System.Web.Http.Filters;  
  5.   
  6. namespace WEBAPI_OPERATION.Filter  
  7. {  
  8.    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]  
  9.     public class CompressFilter : ActionFilterAttribute  
  10.     {  
  11.         public override void OnActionExecuted(HttpActionExecutedContext context)  
  12.         {  
  13.             var acceptedEncoding = context.Response.RequestMessage.Headers.AcceptEncoding.First().Value;  
  14.             if (!acceptedEncoding.Equals("gzip", StringComparison.InvariantCultureIgnoreCase)  
  15.             && !acceptedEncoding.Equals("deflate", StringComparison.InvariantCultureIgnoreCase))  
  16.             {  
  17.                 return;  
  18.             }  
  19.             context.Response.Content = new CompressedContent(context.Response.Content, acceptedEncoding);  
  20.         }  
  21.     }  
  22. }  
We have used [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] which can be used in both the controller level as well as the action method level.

We want API Response to compress. Thus, I write all my logic in "OnActionExecuted" event. This event is executed after execution of any action method.

Now, I will add another class CompressedContent.cs and write the code given below:
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.IO;  
  4. using System.IO.Compression;  
  5. using System.Linq;  
  6. using System.Net;  
  7. using System.Net.Http;  
  8. using System.Threading.Tasks;  
  9. using System.Web;  
  10.   
  11. namespace WEBAPI_OPERATION.Filter  
  12. {  
  13.     public class CompressedContent : HttpContent  
  14.     {  
  15.         private readonly string _encodingType;  
  16.         private readonly HttpContent _originalContent;  
  17.         public CompressedContent(HttpContent content, string encodingType = "gzip")  
  18.         {  
  19.             if (content == null)  
  20.             {  
  21.                 throw new ArgumentNullException("content");  
  22.             }  
  23.             _originalContent = content;  
  24.             _encodingType = encodingType.ToLowerInvariant();  
  25.             foreach (var header in _originalContent.Headers)  
  26.             {  
  27.                 Headers.TryAddWithoutValidation(header.Key, header.Value);  
  28.             }  
  29.             Headers.ContentEncoding.Add(encodingType);  
  30.         }  
  31.         protected override bool TryComputeLength(out long length)  
  32.         {  
  33.             length = -1;  
  34.             return false;  
  35.         }  
  36.         protected override Task SerializeToStreamAsync(Stream stream, TransportContext context)  
  37.         {  
  38.             Stream compressedStream = null;  
  39.             switch (_encodingType)  
  40.             {  
  41.                 case "gzip":  
  42.                     compressedStream = new GZipStream(stream, CompressionMode.Compress, true);  
  43.                     break;  
  44.                 case "deflate":  
  45.                     compressedStream = new DeflateStream(stream, CompressionMode.Compress, true);  
  46.                     break;  
  47.                 default:  
  48.                     compressedStream = stream;  
  49.                     break;  
  50.             }  
  51.             return _originalContent.CopyToAsync(compressedStream).ContinueWith(tsk =>  
  52.             {  
  53.                 if (compressedStream != null)  
  54.                 {  
  55.                     compressedStream.Dispose();  
  56.                 }  
  57.             });  
  58.         }  
  59.     }  
  60. }  
I save this project and add the custom filter attribute in the action method, as follows; and test the API again in Postman.
  1. [RoutePrefix("api/Home")]  
  2.     
  3.    public class HomeController : ApiController  
  4.    {  
  5.        [Route("GetData")]  
  6.        [CompressFilter]  
  7.         
  8.        public async Task<IHttpActionResult> getData()  
  9.        {  
  10.            Stopwatch sw = new Stopwatch();  
  11.            sw.Start();  
  12.            Dictionary<objectobject> dict = new Dictionary<objectobject>();  
  13.            List<Employee> li = new List<Employee>();  
  14.            li.Add(new Employee { id = 2, Name = "Debendra", Id = "A123", Email = "Debendra256@gmail.com" });  
  15.            li.Add(new Employee { id = 3, Name = "Sumit", Id = "A124", Email = "Sumit@gmail.com" });  
  16.            li.Add(new Employee { id = 4, Name = "Jayant", Id = "A125", Email = "jsyant@gmail.com" });  
  17.            li.Add(new Employee { id = 5, Name = "Kumar", Id = "A126", Email = "KR@gmail.com" });  
  18.   
  19.            sw.Stop();  
  20.   
  21.            dict.Add("Details", li);  
  22.            dict.Add("Time", sw.Elapsed);  
  23.   
  24.            return Ok(dict);  
  25.   
  26.        }    
  27.    }  
send

Here is the result. If you check the size in the header, you will get the actual compressed size.
 
send 
This way, we can compress the Web API Response to increase the API performance.