Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Compression filter for Web API

I've been using the compression filter for my MVC actions as detailed here:

http://msdn.microsoft.com/en-us/magazine/gg232768.aspx

I've tried to re-purpose the code to do something similar for Web API, but I've hit a roadblock:

public class CompressAPIAttribute : System.Web.Http.Filters.ActionFilterAttribute
{
    public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext filterContext)
    {
        var preferredEncoding = GetPreferredEncoding(filterContext.Request);
        Stream compressedStream = null;
        // Compress the response accordingly
        var response = filterContext.Response;
        response.Headers.Add("Content-encoding", preferredEncoding.ToString());

        if (preferredEncoding == CompressionScheme.Gzip)
        {
            response.Content = new GZipStream(compressedStream, CompressionMode.Compress); //THIS WON'T WORK
        } 

        if (preferredEncoding == CompressionScheme.Deflate)
        {
            response.Content = new DeflateStream(compressedStream, CompressionMode.Compress); //THIS WON'T WORK
        }
        return;
    }

    enum CompressionScheme
    {
        Gzip = 0,
        Deflate = 1,
        Identity = 2
    }

    private CompressionScheme GetPreferredEncoding(HttpRequestMessage request)
    {
        var acceptableEncoding = request.Headers.AcceptEncoding;

        if (acceptableEncoding.Where(h => h.Value.Contains("gzip")).Count() > 0)
            return CompressionScheme.Gzip;

        if (acceptableEncoding.Where(h => h.Value.Contains("deflate")).Count() > 0)
            return CompressionScheme.Deflate;

        return CompressionScheme.Identity;
    }

Any ideas how I can assign a compressed stream to the response's content?

I should point out this is being hosted in IIS 6.0, which I do not control.

like image 876
Mister Epic Avatar asked Jun 26 '13 13:06

Mister Epic


2 Answers

I think you should not do this in an action filter as the modelbinding stage happens before action filters are executed and during modelbinding the formatters could be reading the stream to deserialize it, in which case it would fail.

If you are using IIS, then do the following to setup compression(The following have some snippets from Scott Hanselman's blog post):

  • Enabled the "Dynamic Compression" feature in IIS.

  • Back in IIS Manager, go to the page for the SERVER, not the SITE. Click on Configuration Editor: enter image description here

  • From the dropdown, select system.webServer/httpCompression: enter image description here

  • Then click on Dynamic Types and now that you're in the list editor, think about what types you want compressed. By default / is False, but you could just turn that on. I chose to be a little more picky and added application/atom+xml, application/json, and application/atom+xml;charset=utf-8 as seen below. It's a little gotcha that application/atom+xml and application/atom+xml;charset=utf-8 are separate entries. Feel free to add what ever mimeTypes you like in here. enter image description here
  • After you've added them and closed the dialog, be sure to click Apply and Restart your IIS Service to load the new module.
  • Now make requests using Accept-Encoding header and you should see the response as expected.

  • EDIT ( in addition to above include "application/json; charset=utf-8" to cover both json formats)

like image 81
Kiran Avatar answered Oct 06 '22 12:10

Kiran


If you are using IIS to setup compression, you can modify the file:

C:\Windows\System32\inetsrv\config\applicationHost.config

Add the following tags

    <dynamicTypes>
        <add mimeType="text/*" enabled="true" />
        <add mimeType="message/*" enabled="true" />
        <add mimeType="application/x-javascript" enabled="true" />
        <add mimeType="application/atom+xml" enabled="true" />
        <add mimeType="application/atom+xml; charset=utf-8" enabled="true" />
        <add mimeType="application/json" enabled="true" />
        <add mimeType="application/json; charset=utf-8" enabled="true" />
        <!--<add mimeType="application/*" enabled="true" />-->
        <add mimeType="*/*" enabled="false" />
        <staticTypes>
        ...
        </staticTypes>
    </dynamicTypes>

in the section:

<httpCompression directory="%SystemDrive%\inetpub\temp\IIS Temporary Compressed Files">
    <scheme name="gzip" dll="%Windir%\system32\inetsrv\gzip.dll" />
        <!-- 
            add here 
        -->
        <staticTypes>
            <add mimeType="text/*" enabled="true" />
            <add mimeType="message/*" enabled="true" />
            <add mimeType="application/x-javascript" enabled="true" />
            <add mimeType="application/atom+xml" enabled="true" />
            <add mimeType="application/xaml+xml" enabled="true" />
            <add mimeType="*/*" enabled="false" />
        </staticTypes>
</httpCompression>

After you've added them restart your IIS Service to load the new module.

There is a possibility to increase the level of compression by modifying the tag:

<scheme name="gzip" dll="%Windir%\system32\inetsrv\gzip.dll"
 staticCompressionLevel="9" dynamicCompressionLevel="9" />

done :)

PS: remember to enable the dynamic compression under path system.webServer/httpCompression in Configuration Editor for IIS Server

like image 1
Jerzy Gebler Avatar answered Oct 06 '22 11:10

Jerzy Gebler