WebApi requests compression using Ext.js and asp.net Mvc4
One of the easiest ways to maximize performance of ajax requests being sent over the network is to use gzip compression. There is already a built in mechanism for compressing requests but it depends on IIS settings of our hosting provider. In our case we will apply custom Delegate Handler using asp.net mvc4 ApiController that is not dependent on any environment specific settings.
If you are wondering if it’s worth to implement this solution, have a look on my test results below:
Depending on the content being sent, we can reduce the request size by an average 50% or more. On the above image, the first request have been compressed while the second one was not.
In order to test our solution we will simply invoke ajax request from Ext.js application running along with self-hosted asp.net mvc4 webapi.
Ext.Ajax.request({ method: 'GET', url: 'api/ExtMvcTest/', success: function (response) { var text = response.responseText; alert('Request completed data length: ' + text.length); }, failure: function (response) { alert('An error occured!'); } });
On the server site we need to create our handler that derives from DelegatingHandler class. By doing that we override http SendAsync function and implement our custom CompressedContent class.
public class APICompressionDelegateHandler : 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 && response.RequestMessage.Headers.AcceptEncoding.Count > 0) { string encodingType = response.RequestMessage.Headers.AcceptEncoding.First().Value; if (response.Content != null) { response.Content = new CompressedContent(response.Content, encodingType); } } return response; }, TaskContinuationOptions.OnlyOnRanToCompletion); } }
Next, we need to override SerializeToStreamAsync function in our CompressedContent object that inherits from HttpContent class to implement actual compression and send it back to the calling delegate.
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(); } }); }
Last thing do to is to register our delegate at application start-up and to define our target controller we get our data from.
public static class WebApiConfig { public static void Register(HttpConfiguration config) { config.Routes.MapHttpRoute( name: "ExtTest", routeTemplate: "api/ExtMvcTest/", defaults: new { controller = "ExtMvcTest", action = "GetTestData" }); config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "Controllers/{controller}/{id}", defaults: new { id = RouteParameter.Optional } ); config.MessageHandlers.Add(new APICompressionDelegateHandler()); //enable API requests compression } }
This is how our solution files look like:
I have added fully working project example below. Please be aware that using compression may overload your server if used improperly. It is advisable to use it mainly for dashboards where the response time is the highest priority.
ExtMvc