In one of my articles, I have discussed how to compress Web API Response, using DotNet Zip. You can find the link below:
- Data size is reduced.
- 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:
- [RoutePrefix("api/Home")]
-
- public class HomeController : ApiController
- {
- [Route("GetData")]
-
-
- public async Task<IHttpActionResult> getData()
- {
- Stopwatch sw = new Stopwatch();
- sw.Start();
- Dictionary<object, object> dict = new Dictionary<object, object>();
- List<Employee> li = new List<Employee>();
- li.Add(new Employee { id = 2, Name = "Debendra", Id = "A123", Email = "[email protected]" });
- li.Add(new Employee { id = 3, Name = "Sumit", Id = "A124", Email = "[email protected]" });
- li.Add(new Employee { id = 4, Name = "Jayant", Id = "A125", Email = "[email protected]" });
- li.Add(new Employee { id = 5, Name = "Kumar", Id = "A126", Email = "[email protected]" });
-
- sw.Stop();
-
- dict.Add("Details", li);
- dict.Add("Time", sw.Elapsed);
-
- return Ok(dict);
-
- }
- }
Now, open the Postman and test the response of this API, as shown in the following screen:
Now, check the actual size of the Response.
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:
- using System.Collections.Generic;
- using System.Linq;
- using System.Web;
- using System.Web.Http.Filters;
-
- namespace WEBAPI_OPERATION.Filter
- {
- [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
- public class CompressFilter : ActionFilterAttribute
- {
- public override void OnActionExecuted(HttpActionExecutedContext context)
- {
- var acceptedEncoding = context.Response.RequestMessage.Headers.AcceptEncoding.First().Value;
- if (!acceptedEncoding.Equals("gzip", StringComparison.InvariantCultureIgnoreCase)
- && !acceptedEncoding.Equals("deflate", StringComparison.InvariantCultureIgnoreCase))
- {
- return;
- }
- context.Response.Content = new CompressedContent(context.Response.Content, acceptedEncoding);
- }
- }
- }
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:
- using System;
- using System.Collections.Generic;
- using System.IO;
- using System.IO.Compression;
- using System.Linq;
- using System.Net;
- using System.Net.Http;
- using System.Threading.Tasks;
- using System.Web;
-
- namespace WEBAPI_OPERATION.Filter
- {
- public class CompressedContent : HttpContent
- {
- private readonly string _encodingType;
- private readonly HttpContent _originalContent;
- public CompressedContent(HttpContent content, string encodingType = "gzip")
- {
- if (content == null)
- {
- throw new ArgumentNullException("content");
- }
- _originalContent = content;
- _encodingType = encodingType.ToLowerInvariant();
- foreach (var header in _originalContent.Headers)
- {
- Headers.TryAddWithoutValidation(header.Key, header.Value);
- }
- 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;
- switch (_encodingType)
- {
- case "gzip":
- compressedStream = new GZipStream(stream, CompressionMode.Compress, true);
- break;
- case "deflate":
- compressedStream = new DeflateStream(stream, CompressionMode.Compress, true);
- break;
- default:
- compressedStream = stream;
- break;
- }
- return _originalContent.CopyToAsync(compressedStream).ContinueWith(tsk =>
- {
- if (compressedStream != null)
- {
- compressedStream.Dispose();
- }
- });
- }
- }
- }
I save this project and add the custom filter attribute in the action method, as follows; and test the API again in Postman.
- [RoutePrefix("api/Home")]
-
- public class HomeController : ApiController
- {
- [Route("GetData")]
- [CompressFilter]
-
- public async Task<IHttpActionResult> getData()
- {
- Stopwatch sw = new Stopwatch();
- sw.Start();
- Dictionary<object, object> dict = new Dictionary<object, object>();
- List<Employee> li = new List<Employee>();
- li.Add(new Employee { id = 2, Name = "Debendra", Id = "A123", Email = "[email protected]" });
- li.Add(new Employee { id = 3, Name = "Sumit", Id = "A124", Email = "[email protected]" });
- li.Add(new Employee { id = 4, Name = "Jayant", Id = "A125", Email = "[email protected]" });
- li.Add(new Employee { id = 5, Name = "Kumar", Id = "A126", Email = "[email protected]" });
-
- sw.Stop();
-
- dict.Add("Details", li);
- dict.Add("Time", sw.Elapsed);
-
- return Ok(dict);
-
- }
- }
Here is the result. If you check the size in the header, you will get the actual compressed size.
This way, we can compress the Web API Response to increase the API performance.