Friday, March 28, 2008 3:12 AM Kazi Manzur Rashid

ASP.NET MVC Action Filter - Caching and Compression

Caching plays a major role in developing highly scalable web applications. We can cache any http get request in the user browser for a predefined time, if the user request the same URL in that predefined time the response will be loaded from the browser cache instead of the server. You can archive the same in ASP.NET MVC application with the following action filter:

using System;
using System.Web;
using System.Web.Mvc;

public class CacheFilterAttribute : ActionFilterAttribute
{
    /// <summary>
    /// Gets or sets the cache duration in seconds. The default is 10 seconds.
    /// </summary>
    /// <value>The cache duration in seconds.</value>
    public int Duration
    {
        get;
        set;
    }

    public CacheFilterAttribute()
    {
        Duration = 10;
    }

    public override void OnActionExecuted(FilterExecutedContext filterContext)
    {
        if (Duration <= 0) return;

        HttpCachePolicyBase cache = filterContext.HttpContext.Response.Cache;
        TimeSpan cacheDuration = TimeSpan.FromSeconds(Duration);

        cache.SetCacheability(HttpCacheability.Public);
        cache.SetExpires(DateTime.Now.Add(cacheDuration));
        cache.SetMaxAge(cacheDuration);
        cache.AppendCacheExtension("must-revalidate, proxy-revalidate");
    }
}

You can apply the filter in your Controller action method like the following.

[CacheFilter(Duration = 60)]
public void Category(string name, int? page)

The following shows the screen-shot in firebug  when cache filter is not applied:

and this is the screen-shot when the cache filter is applied:

Another important thing is compression. Now a days, all modern browsers accept compressed contents and it saves huge bandwidth. You can apply the following action filter to compress your response in your  ASP.NET MVC application:

using System.Web;
using System.Web.Mvc;

public class CompressFilter : ActionFilterAttribute
{
    public override void OnActionExecuting(FilterExecutingContext filterContext)
    {
        HttpRequestBase request = filterContext.HttpContext.Request;

        string acceptEncoding = request.Headers["Accept-Encoding"];

        if (string.IsNullOrEmpty(acceptEncoding)) return;

        acceptEncoding = acceptEncoding.ToUpperInvariant();

        HttpResponseBase response = filterContext.HttpContext.Response;

        if (acceptEncoding.Contains("GZIP"))
        {
            response.AppendHeader("Content-encoding", "gzip");
            response.Filter = new GZipStream(response.Filter, CompressionMode.Compress);
        }
        else if (acceptEncoding.Contains("DEFLATE"))
        {
            response.AppendHeader("Content-encoding", "deflate");
            response.Filter = new DeflateStream(response.Filter, CompressionMode.Compress);
        }
    }
}

Just decorate your controller action with this filter:

[CompressFilter]
public void Category(string name, int? page)

The following shows when compression is not applied:

and this is the screen-shot when the compress filter is applied:

You can also apply both these filter in the same action method, like the following:

[CompressFilter(Order = 1)]
[CacheFilter(Duration = 60, Order = 2)]
public void Category(string name, int? page)

And this is the screen-shot:


Enjoy!!!

Download: Source.zip

kick it on DotNetKicks.com

Filed under: ,

Comments

# re: ASP.NET MVC Action Filter - Caching and Compression

Thursday, March 27, 2008 11:11 PM by Stuart

Thanks a bunch for posting these.  Your whole MVC series has been great

# re: ASP.NET MVC Action Filter - Caching and Compression

Friday, March 28, 2008 10:37 AM by Muhammad Mosa

Simple and clear, and easy to follow!

Thank you Kazi

# re: ASP.NET MVC Action Filter - Caching and Compression

Friday, March 28, 2008 6:58 PM by tgmdbm

Nice, I like the compression filter, but the "Browser Cache" filter doesn't seem to do anything.

my Cache-Control header is "private, must-revalidate, proxy-revalidate, max-age=60" even tho we're obviously trying to set it to "public ...". I don't understand that.

Maybe that is affecting it but no matter how many times i request my page the Request Headers don't contain a Modified-Since entry.

With browser cache, the browser should send the request asking if the file has been modified since the date of the cached version. But in the case of dynamic pages the server should always say the file has just been modified so would send the page down every time anyway.

We'd have to send down a false modified date to get the browser to use its cached version.

# re: ASP.NET MVC Action Filter - Caching and Compression

Saturday, March 29, 2008 4:35 AM by Kazi Manzur Rashid

@Everyone: Thanks.

@tgmdbm: I think I need to clarify more on Browser caching. Content can be cached by both Cach-Control header(The CacheFilter in this case) and ETag(Which I guess you are mentioning). The advantage of Cache-Control over ETag is, the browser does not send a request to server to check if the content has been modified in that cache priod, but for ETag the browser sends a request to Server to check if the content is modified and if the content is not modified the server sends a 304(Not Modified). So using Cache-Control you are reducing the extra Roundtrip of the server. Also ETag is more appropriate for static files like image, css, js where the web server aumatically genarates the values of ETag.

The following is an excellent article on Http Cache:

www.mnot.net/cache_docs

# re: ASP.NET MVC Action Filter - Caching and Compression

Wednesday, April 2, 2008 2:41 PM by azamsharp

Very very nice!

Keep up the good work!

# re: ASP.NET MVC Action Filter - Caching and Compression

Wednesday, April 2, 2008 5:23 PM by maddog

Hey What utility are you using in the screenies???

# re: ASP.NET MVC Action Filter - Caching and Compression

Saturday, April 5, 2008 11:18 PM by Omar AL Zabir

Very useful code. Thanks for introducing these.

One suggestion about compression, it's always better to use IIS 6.0's built in compression over any ASP.NET code, whether a filter or an HttpModule when it comes to scalability. IIS Native compressor always performs better than the .NET components. Moreover, you can increase/decrease compression ratio and balance speed over size.

See here how to turn on IIS compression on dynamic ASP.NET requests:

msmvps.com/.../iis-6-compression-quickest-and-effective-way-to-do-it-for-asp-net-compression.aspx

However, in IIS, you specify which extensions to compress. Now that MVC is almost extensionless, this introduces an interesting challenge - how will IIS know which extension to compress?

# re: ASP.NET MVC Action Filter - Caching and Compression

Sunday, April 6, 2008 8:30 PM by Kazi Manzur Rashid

@azamsharp: Thanks. Sure I will.

@Mad Dog: Print Screen + MS Paint.

@Omar: Thanks for comments, Yes, I agree IIS Compression is always better but most of the shared hosting does not allow to modify the IIS metabase. For those who are running dedicated IIS6 can surly turn on the compression like you mentoned in your blog.

# re: ASP.NET MVC Action Filter - Caching and Compression

Saturday, April 26, 2008 6:31 AM by yang

Is it possible to do this on the controller level. Say I want to compress all the actions of the controller. Is there any other way to apply this attribute to every action of the controller?

# re: ASP.NET MVC Action Filter - Caching and Compression

Saturday, April 26, 2008 9:30 PM by Kazi Manzur Rashid

@yang :

Sure, just apply it in your controller instead of the action methods.

# re: ASP.NET MVC Action Filter - Caching and Compression

Wednesday, May 21, 2008 11:47 AM by jamil

I have xml file I loaded into cache

is there any way I can see the cache content and modify it

if you have some example please send

thanks

# re: ASP.NET MVC Action Filter - Caching and Compression

Monday, June 23, 2008 3:59 PM by shaun

You should update the sample to change the FilterExecutedContext (ActionExecutedContext) and FilterExecutingContext (ActionExecutingContext) to reflect the changes in preview 3.

I also made a minor change to the caching filter to set the cacheability to HttpCacheability.NoCache when the duration is <= 0. Not sure if that was necessary but I wanted to make sure that a few actions were not cached.

Other than that, thanks for the great post!

# re: ASP.NET MVC Action Filter - Caching and Compression

Thursday, July 24, 2008 8:05 AM by Steve Gentile

Excellent on the compression part!

I updated for Preview 4 - one minor change:

public override void OnActionExecuting(ActionExecutingContext filterContext)

Now accepts a 'ActionExecutingContext'

Thanks again!

# re: ASP.NET MVC Action Filter - Caching and Compression

Wednesday, March 11, 2009 6:23 PM by dkarantonis

Great article!

Any idea of the reason that one should implement the action filter you suggest for caching output instead of using a cache profile on the web.config file and call the  [OutputCache(CacheProfile = "myCahceProfile")] on the action?

This way, any changes to the caching profile (duration for example) can be implemented in the web.config file without even recompiling the application.

thanks

# re: ASP.NET MVC Action Filter - Caching and Compression

Friday, April 17, 2009 4:53 AM by DevareattyVaH

nice, really nice!