2

Given the generic handler:

<%@ WebHandler Language="C#" Class="autocomp" %>

using System;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.UI;

public class autocomp : IHttpHandler {

    public void ProcessRequest (HttpContext context) {

        context.Response.ContentType = "application/json";
        context.Response.BufferOutput = true;

        var searchTerm = (context.Request.QueryString["name_startsWith"] + "").Trim();

        context.Response.Write(searchTerm);
        context.Response.Write(DateTime.Now.ToString("s"));

        context.Response.Flush();
    }

    public bool IsReusable {
        get {
            return false;
        }
    }

}

How would I server side cache this file for 1 hour based on the name_startsWith query string parameter? With web user controls it's easy:

<%@ OutputCache Duration="120" VaryByParam="paramName" %>

But I've been looking around for a while to do the same with a generic handler (ashx) file and can't find any solutions.

Tom Gullen
  • 61,249
  • 84
  • 283
  • 456
  • You would cache on client side using [this stuff](http://stackoverflow.com/questions/1109768/how-to-use-output-caching-on-ashx-handler) and then use server side cache for the actual data using HttpContext.Cache when you're going to fetch the data. But you won't be able to use any kind of output cache for this. Also, remember to make sure your HttpContext.Cache code is thread-safe. ;) – Tombala Jul 15 '13 at 15:28
  • Edited my answer below to answer the changes to the post. – Steven V Jul 15 '13 at 15:49

4 Answers4

8

With the code you've provided you're telling the end user browser to cache the results for 30 minutes, so you aren't doing any server side caching.

If you want to cache the results server side you're probably looking for HttpRuntime.Cache. This would allow you to insert an item into a cache that is globally available. Then on the page load you would want to check the existence of the cached item, then if the item doesn't exist or is expired in the cache, go to the database and retrieve the objects.

EDIT

With your updated code sample, I found https://stackoverflow.com/a/6234787/254973 which worked in my tests. So in your case you could do:

public class autocomp : IHttpHandler
{
    public void ProcessRequest(HttpContext context)
    {
        OutputCachedPage page = new OutputCachedPage(new OutputCacheParameters
        {
            Duration = 120,
            Location = OutputCacheLocation.Server,
            VaryByParam = "name_startsWith"
        });

        page.ProcessRequest(HttpContext.Current);

        context.Response.ContentType = "application/json";
        context.Response.BufferOutput = true;

        var searchTerm = (context.Request.QueryString["name_startsWith"] + "").Trim();

        context.Response.Write(searchTerm);
        context.Response.Write(DateTime.Now.ToString("s"));
    }

    public bool IsReusable
    {
        get
        {
            return false;
        }
    }
    private sealed class OutputCachedPage : Page
    {
        private OutputCacheParameters _cacheSettings;

        public OutputCachedPage(OutputCacheParameters cacheSettings)
        {
            // Tracing requires Page IDs to be unique.
            ID = Guid.NewGuid().ToString();
            _cacheSettings = cacheSettings;
        }

        protected override void FrameworkInitialize()
        {
            base.FrameworkInitialize();
            InitOutputCache(_cacheSettings);
        }
    }
}
Community
  • 1
  • 1
Steven V
  • 16,357
  • 3
  • 63
  • 76
  • `OutputCachedPage` is a custom sealed class that is at the bottom of the code sample. No assemblies/namespaces needed. – Steven V Jul 17 '13 at 15:00
  • Sorry, silly me. I copied and pasted that handler into my project, but the output isn't caching. It just doesn't seem to work. – Tom Gullen Jul 17 '13 at 15:02
  • @TomGullen My apologies, remove the `context.Response.Flush()` the end of `ProcessRequest` and it's working for me again. I setup a quick sample at http://outputcachetest.azurewebsites.net/CacheTest.ashx?name_startsWith=t vs http://outputcachetest.azurewebsites.net/CacheTest.ashx?name_startsWith=anyvaluehere – Steven V Jul 17 '13 at 15:10
  • no need to apologise, really appreciate the help! Removing the flush works perfectly. Thanks for taking the time to help. – Tom Gullen Jul 18 '13 at 12:23
1

For multiple query string parameters

public class test : IHttpHandler
{

    public void ProcessRequest(HttpContext context)
    {
        OutputCachedPage page = new OutputCachedPage(new OutputCacheParameters
        {
            Duration = 120,
            Location = OutputCacheLocation.Server,
            VaryByParam = "name;city"
        });

        page.ProcessRequest(HttpContext.Current);

        context.Response.ContentType = "application/json";
        context.Response.BufferOutput = true;

        var searchTerm = (context.Request.QueryString["name"] + "").Trim();
        var searchTerm2 = (context.Request.QueryString["city"] + "").Trim();

        context.Response.Write(searchTerm+" "+searchTerm2+" ");
        context.Response.Write(DateTime.Now.ToString("s"));
    }

    public bool IsReusable
    {
        get
        {
            return false;
        }
    }
    private sealed class OutputCachedPage : Page
    {
        private OutputCacheParameters _cacheSettings;

        public OutputCachedPage(OutputCacheParameters cacheSettings)
        {
            // Tracing requires Page IDs to be unique.
            ID = Guid.NewGuid().ToString();
            _cacheSettings = cacheSettings;
        }

        protected override void FrameworkInitialize()
        {
            base.FrameworkInitialize();
            InitOutputCache(_cacheSettings);
        }
    }
}
0

IIS does not use Max Age to cache anything as it is not HTTP PROXY.

That is because you are not setting Last Modified Date Time of some dependent file. IIS needs a cache dependency (file dependency, so that it can check the last update time) and compare it with cache. IIS does not work as HTTP Proxy, so it will not cache items for 30 seconds, instead IIS only updates cache based on some sort of date time or some cache variable.

You can add cache dependency two says, File Dependency and Sql Cache Dependency.

How dynamic caching works in IIS, lets say you have an html file. IIS considers a static html text as cachable file and it will gzip it and put cached copy in its cache. If last update time for static html is older then cache time, then it will use cache. If the file was modified, IIS will find that last update time of html is greater then cache time, so it will reset the cache.

For dynamic content, you have to plan your caching accordingly. If you are serving a content based on some row stored in SQL table, then you should keep track of last update time on that row and add cache dependency on IIS along with SQL to query the last update time of item you are trying to cache.

Akash Kava
  • 39,066
  • 20
  • 121
  • 167
0

To cache file, such as .js, .css or other you need to put it to the context.cache. Example:

    public void ProcessRequest(HttpContext context)
    {
        var cachedResult = context.Cache.Get(context.Request.Path);

        if (cachedResult != null && cachedResult.GetType() == typeof(VueFileRequestResult))
        {
            RequestedFileResponce(context, cachedResult as VueFileRequestResult);
            return;
        }
        
        // SOME ACTIONS WITH NON-CACHED FILE

        var fileContent = System.IO.File.ReadAllBytes(filePath);
        var result = new VueFileRequestResult(contentType.GetDescription(), fileContent);

        RequestedFileResponce(context, result);

        var expirationDate = DateTime.Now.AddYears(1);
        var dependency = new CacheDependency(filePath);
        context.Cache.Add(context.Request.Path, result, dependency, expirationDate, TimeSpan.Zero, CacheItemPriority.Low, null);

        return;
    }