Compress requests from Angular to web API

One possibility is to use industry standard algorithms for compressing data such as gzip. They provide very good compression for raw strings and if you are sending large objects to the server then you can definitely gain performance by reducing the size of your requests. Not to mention the benefits you get when your app runs on mobile devices with limited bandwidth.

But enough of chattering, let's get to practice. The biggest challenge here is to generate valid gzip request in javascript. One possibility is to read the specification of this format and roll your own implementation or use some existing library. One that I find particularly interesting is pako.

It's trivial to install in your application using bower by simply issuing the following command:

bower install pako

Now let's see how a sample request would look from a client perspective. Let's suppose that you would like to send the following JSON to the server (either as POST or PUT verbs):

{ my: 'super', puper: [456, 567], awesome: 'pako' }

You could achieve that as simply as using the plain XMLHttpRequest object available in modern browsers (read below if you are interested in an Angular specific solution):

<script src="bower_components/pako/dist/pako.min.js"></script>
<script>
    var xhr = new XMLHttpRequest();
    xhr.open('POST', '/api/myresource', true);

    // Indicate to the serve that you will be sending data in JSON format
    xhr.setRequestHeader('Content-Type', 'application/json');
    // Indicate to the server that your data is encoded using the gzip format
    xhr.setRequestHeader('Content-Encoding', 'gzip');

    xhr.onreadystatechange = function (e) {
        if (this.readyState == 4 && this.status == 200) {
            alert('We have just successfully sent a gzip encoded payload to the server');
        }
    };

    var data = { my: 'super', puper: [456, 567], awesome: 'pako' };
    var binaryString = pako.gzip(JSON.stringify(data));
    xhr.send(binaryString);
</script>

and since you asked about an Angular request, let's Angularify this sample AJAX request using the native $http object:

<!DOCTYPE html>
<html>
<head>
    <title></title>
    <meta charset="utf-8" />
</head>
<body ng-app="myapp">
    <div ng-controller="HomeController"></div>
    <script src="bower_components/pako/dist/pako.min.js"></script>
    <script src="bower_components/angular/angular.min.js"></script>
    <script>
        angular.module('myapp', []).controller('HomeController', ['$http', function ($http) {
            var data = { my: 'super', puper: [456, 567], awesome: 'pako' };
            var binaryString = pako.gzip(JSON.stringify(data));
            var req = {
                method: 'POST',
                url: '/api/myresource',
                headers: {
                    'Content-Type': 'application/json',
                    'Content-Encoding': 'gzip'
                },
                data: binaryString,
                transformRequest: []
            }

            $http(req).then(function (result) {
                alert('We have just successfully sent a gzip encoded payload to the server');
            }, function () {
                alert('OOPS, something went wrong, checkout the Network tab in your browser for more details');
            });
        }]);
    </script>
</body>
</html>

OK, basically we have now covered the client side sending part which uses an AJAX request and specifies the proper Content-Encoding request header.

Now let's deal with the server side part. Let's suppose that you use Web API 2 hosted in IIS.

So basically you would have a Startup class in your ASP.NET application that will bootstrap your Web API:

public class Startup
{
    public void Configuration(IAppBuilder app)
    {
        var config = GlobalConfiguration.Configuration;
        config.MapHttpAttributeRoutes();
        app.UseWebApi(config);
        config.EnsureInitialized();
    }
}

and then obviously you have a view model to map your payload to:

public class MyViewModel
{
    public string My { get; set; }
    public int[] Puper { get; set; }
    public string Awesome { get; set; }
}

and a Web API controller that will serve the purpose of a server side handler of your AJAX requests:

public class TestController : ApiController
{
    [HttpPost]
    [Route("api/myresource")]
    public HttpResponseMessage Post(MyViewModel viewModel)
    {
        // We will simply echo out the received request object to the response
        var response = Request.CreateResponse(HttpStatusCode.OK, viewModel);
        return response;
    }
}

So far so good. Unfortunately Web API doesn't support gzip request encoding out of the box. But since this is a pretty extensible framework all you have to do is write a custom delegating handler that will know how to unzip the request coming from the client.

Let's start by writing a custom HttpContent:

public class DecompressedHttpContent: HttpContent
{
    private readonly HttpContent _content;
    public DecompressedHttpContent(HttpContent content)
    {
        _content = content;
        foreach (var header in _content.Headers)
        {
            Headers.TryAddWithoutValidation(header.Key, header.Value);
        }
    }

    protected override async Task SerializeToStreamAsync(Stream stream, TransportContext context)
    {
        using (var originalStream = await _content.ReadAsStreamAsync())
        using (var gzipStream = new GZipStream(originalStream, CompressionMode.Decompress))
        {
            await gzipStream.CopyToAsync(stream);
        }
    }

    protected override bool TryComputeLength(out long length)
    {
        length = -1;
        return false;
    }
}

and then our delegating handler:

public class GzipDecompressionHandler : DelegatingHandler
{
    protected override async Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request,
        CancellationToken cancellationToken
    )
    {
        var isCompressedPayload = request.Content.Headers.ContentEncoding.Any(x => string.Equals(x, "gzip", StringComparison.InvariantCultureIgnoreCase));
        if (!isCompressedPayload)
        {
            return await base.SendAsync(request, cancellationToken);
        }

        request.Content = new DecompressedHttpContent(request.Content);
        return await base.SendAsync(request, cancellationToken);
    }
}

All that's left now is to register this custom handler in our Startup class:

config.MessageHandlers.Add(new GzipDecompressionHandler());

And that's pretty much it. Now when the TestController.Post action is called from client side AJAX request, the input body will contain the proper headers and our delegating handler will take care of decoding it so that when the Post action is called you would get the expected view model already deserialized.

Now to recap you should be aware that for small requests such as the one shown in this example you probably won't gain much by using gzip - you could even make things worse because of the magic gzip numbers that will add to the payload. But for bigger requests this approach will definitely boost reduce your requests size and I strongly encourage you to use gzip.

And here's the result of this effort:


Comments

  1. Nolan

    • 2019/4/7

    One possibility is to use industry standard algorithms for compressing data such as gzip . They provide very good compression for raw strings and if you are 

  2. Harry

    • 2017/12/24

    Unfortunately Web API doesn't support gzip request encoding out of the box. But since this is a pretty extensible framework all you have to do is write a custom delegating handler that will know how to unzip the request coming from the client.

  3. Mark

    • 2019/1/7

    Another way to compress content in Web API is by using the Microsoft.AspNet.WebApi.MessageHandlers.Compression package. You can install it via 

  4. Jagger

    • 2016/12/26

    Therefore, no worries now; here is your host who will teach you how to compress an image in angular before sending it to an API. So, to make your request work fine and make your compression easy

  5. Kason

    • 2017/10/12

    Now all Web API requests, will return a GZIP compressed response if requested in the header. webapigzip_02. The NuGet package is very easy to 

  6. Carbone

    • 2015/7/8

    This sends an HTTP GET request to the npm api for a list of packages that belong to the @angular scope, then assigns the total returned in the response to the local property totalAngularPackages. The response type is set to <any> so it handle any properties returned in the response.

  7. Zain

    • 2019/6/28

    I am trying to optimize bandwidth usage by compressing requests from my angular client to a AspNet Web API. Is there any way to achieve this?

  8. Miles

    • 2020/1/10

    Another way to compress content in Web API is by using the Microsoft.AspNet.WebApi.MessageHandlers.Compression package. You can install it via NuGet and use it to compress content.

  9. Spencer

    • 2017/3/1

    In this article, we are going to learn how to compress Web API image of the same request with compressed data using gzip compression.

  10. Sinani

    • 2018/9/15

    If you find yourself in a situation where you are sending large HTTP request payloads from the web browser to your API, you’ve probably made an architectural or design mistake.

  11. Dayton

    • 2018/5/19

    In this tutorial I will show how to send compressed content to a web api. A lot of time when we develop a client application to consume a 

  12. Rocco

    • 2017/12/21

    Sometimes you need to upload files to your server from an Angular application to a Web API method. There are many ways to upload a file. In this article, I'm going to present a method that works well for small files, up to about one or two megabytes in size. You're going to build two projects; a .NET Core Web API project and an Angular project.

  13. Landen

    • 2019/9/10

    I'm using HttpClient to send the requests and Asp.net web api as service. I saw the below link but it doesn't explain about how the decompress 

  14. Graysen

    • 2019/3/6

    In this Angular FormData tutorial, we will understand how to use Angular 12 HttpClient API to Post FormData to a web server. Forms are an essential part of any web or mobile applications, and Forms allow us to gather data from the users and send that data to the webserver. HTTP requests are used to […]

  15. Axl

    • 2016/12/21

    Submit a request to the sample app with the Accept-Encoding: br header (Brotli compression) and observe that the response is compressed. The 

  16. De Santis

    • 2019/8/24

    NET Core web API application. Open Project.json and include following nuget By default, compression is disabled for HTTPS requests.

  17. Phillip

    • 2016/7/3

    Another example is a REST web service that we use. We have to send frequent PUT requests with large XML files (10+ MB) and would definitely see some bandwidth/ 

Comments are closed.

Recent Posts