2. The HttpCachePolicy Class
The HttpCachePolicy class is a programming interface alternative to using the @OutputCache
directive. It provides direct methods to set cache-related HTTP
headers, which you could also control to some extent by using the
members of the HttpResponse object.
Properties of the HttpCachePolicy Class
Table 3 shows the properties of the HttpCachePolicy class.
Table 3. HttpCachePolicy Class Properties
Property | Description |
---|
VaryByHeaders | Gets an object of type HttpCacheVaryByHeaders, representing the list of all HTTP headers that will be used to vary cache output |
VaryByParams | Gets an object of type HttpCacheVaryByParams, representing the list of parameters received by a GET or POST request that affect caching |
When a cached page has several vary-by headers
or parameters, a separate version of the page is available for each HTTP
header type or parameter name.
Methods of the HttpCachePolicy Class
Table 4 shows the methods of the HttpCachePolicy class.
Table 4. HttpCachePolicy Class Methods
Method | Description |
---|
AddValidationCallback | Registers a callback function to be used to validate the page output in the server cache before returning it. |
AppendCacheExtension | Appends the specified text to the Cache-Control HTTP header. The existing text is not overwritten. |
SetAllowResponseInBrowserHistory | When this setting is true, the response is available in the browser’s History cache, regardless of the HttpCacheability option set on the server. |
SetCacheability | Sets the Cache-Control HTTP header to any of the values taken from the HttpCacheability enumeration type. |
SetETag | Sets the ETag header to the specified string. The ETag header is a unique identifier for a specific version of a document. |
SetETagFromFileDependencies | Sets the ETag
header to a string built by combining and then hashing the last
modified date of all the files upon which the page is dependent. |
SetExpires | Sets the Expires header to an absolute date and time. |
SetLastModified | Sets the Last-Modified HTTP header to a particular date and time. |
SetLastModifiedFromFileDependencies | Sets the Last-Modified HTTP header to the most recent timestamps of the files upon which the page is dependent. |
SetMaxAge | Sets the max-age attribute on the Cache-Control header to the specified value. The sliding period cannot exceed one year. |
SetNoServerCaching | Disables server output caching for the current response. |
SetNoStore | Sets the Cache-Control: no-store directive. |
SetNoTransforms | Sets the Cache-Control: no-transforms directive. |
SetOmitVaryStar | If set to true, causes HttpCachePolicy to ignore the * value in VaryByHeaders. Not supported by ASP.NET 1.x. |
SetProxyMaxAge | Sets the Cache-Control: s-maxage header. |
SetRevalidation | Sets the Cache-Control header to either must-revalidate or proxy-revalidate. |
SetSlidingExpiration | Sets cache expiration to sliding. When cache expiration is set to sliding, the Cache-Control header is renewed at each response. |
SetValidUntilExpires | Specifies whether the ASP.NET cache should ignore HTTP Cache-Control headers sent by some browsers to evict a page from the cache. If this setting is true, the page stays in the cache until it expires. |
SetVaryByCustom | Sets the Vary HTTP header to the specified text string. |
Most methods of the HttpCachePolicy class let you control the values of some HTTP headers that relate to the browser cache. The AddValidationCallback
method, on the other hand, provides a mechanism to programmatically
check the validity of page output in the server cache before it is
returned from the cache.
Server Cache-Validation Callback
Before the response is served from the ASP.NET
cache, all registered handlers are given a chance to verify the validity
of the cached page. If at least one handler marks the cached page as
invalid, the entry is removed from the cache and the request is served
as if it were never cached. The signature of the callback function looks
like this:
public delegate void HttpCacheValidateHandler(
HttpContext context,
object data,
ref HttpValidationStatus validationStatus
);
The first argument denotes the context of the
current request, whereas the second argument is any user-defined data
the application needs to pass to the handler. Finally, the third
argument is a reference to a value from the HttpValidationStatus enumeration. The callback sets this value to indicate the result of the validation. Acceptable values are IgnoreThisRequest, Invalid, and Valid. In the case of IgnoreThisRequest, the cached resource is not invalidated but the request is served as if no response was ever cached. If the return value is Invalid, the cached page is not used and gets invalidated. Finally, if the return value is Valid, the cached response is used to serve the request.
3. Caching Multiple Versions of a Page
Depending on the application context from which a
certain page is invoked, the page might generate different results. The
same page can be called to operate with different parameters, can be
configured using different HTTP headers, can produce different output
based on the requesting browser, and so forth.
ASP.NET allows you to cache multiple versions of a page response; you can distinguish versions by GET and POST parameters, HTTP headers, browser type, custom strings, and control properties.
Vary By Parameters
To vary output caching by parameters, you can use either the VaryByParam attribute of the @OutputCache directive or the VaryByParams property on the HttpCachePolicy class. If you proceed declaratively, use the following syntax:
<% @OutputCache Duration="60" VaryByParam="employeeID" %>
Note that the VaryByParam attribute is mandatory; if you don’t want to specify a parameter to vary cached content, set the value to None. If you want to vary the output by all parameters, set the attribute to *. When the VaryByParam
attribute is set to multiple parameters, the output cache contains a
different version of the requested document for each specified
parameter. Multiple parameters are separated by a semicolon. Valid
parameters to use are items specified on the GET query string or parameters set in the body of a POST command.
If you want to use the HttpCachePolicy class to define caching parameters, first set the expiration and the cacheability of the page using the SetExpires and SetCacheability methods. Next, set the VaryByParams property as shown here:
Response.Cache.SetExpires(DateTime.Now.AddSeconds(60));
Response.Cache.SetCacheability(HttpCacheability.Public);
Response.Cache.VaryByParams["employeeid;lastname"] = true;
This code snippet shows how to vary page output based on the employee ID and the last name properties. Note that the Cache property on the HttpResponse class is just an instance of the HttpCachePolicy type.
Dealing with Postback Pages
Most ASP.NET pages do postbacks. The page in Figure 1 (sqldepoutputcache.aspx)
is no exception. The page has two key features: it is dependent on
changes to the Customers table in the Northwind database, and it has a
cache duration of 30 seconds. Furthermore, the drop-down list (named Countries) has auto-postback functionality and places a POST request for the same page whenever you change the selection.
With VaryByParam set to None,
you’ll wait 30 seconds (or whatever the cache duration is) to have your
country selection processed. It is a bit frustrating: no matter which
selection you make, it is blissfully ignored and the same page is
displayed. Worse yet, if you test the page under the Visual Studio .NET
Web Development server, after a couple of attempts you get a “page not
found” error. If you test the page under IIS, you are repeatedly served
the same page response, regardless of the selection made.
Two points clearly emerge from this discussion.
First, pages with static content are a better fit for caching than
interactive pages. Second, the postback mechanism returns a bunch of
form parameters. You need to vary the cached copies of the page by the
most critical of them. The sample page in Figure 16-7
has a few hidden fields (try snooping in its HTML source), such as
__VIEWSTATE and __LASTFOCUS, and the drop-down list. Varying by view
state makes no sense at all, but varying by the selected countries is
exactly what we need:
<%@ OutputCache VaryByParam="Countries" Duration="30"
SqlDependency="Northwind:Customers" %>
The directive stores each country-specific page
for 30 seconds unless a change occurs in the Customers database. In
such a case, all the cached versions of the page will be invalidated.
The
bottom line is that enabling page output caching might not be painless
for interactive pages. It is free of pain and charge for relatively
static pages like those describing a product, a customer, or some news.
Caution
A cached ASP.NET page is
served more quickly than a processed page, but not as quickly as a
static HTML page. However, the response time is nearly identical if the
ASP.NET page is kernel-cached in IIS 6.0. Unfortunately, IIS 6.0 doesn’t
store in its kernel-level cache ASP.NET pages requested via a POST verb
and, more importantly, pages with VaryByParam or VaryByHeader. In the end, postback pages have very few chances to be cached in the IIS kernel. They are cached in the ASP.NET Cache, in downstream caching servers, or both. |
Vary By Headers
The VaryByHeader attribute and the HttpCachePolicy’s VaryByHeaders property allow you to cache multiple versions of a page, according to the value of one or more HTTP headers that you specify.
If you want to cache pages by multiple headers,
include a semicolon-delimited list of header names. If you want to
cache a different version of the page for each different header value,
set the VaryByHeader attribute to an asterisk *.
For example, the following declaration caches for one-minute pages
based on the language accepted by the browser. Each language will have a
different cached copy of the page output.
<%@ OutputCache Duration="60" VaryByParam="None"
VaryByHeader="Accept-Language" %>
If you opt for a programmatic approach, here’s the code to use that leverages the VaryByHeaders property of the HttpCachePolicy class:
Response.Cache.VaryByHeaders["Accept-Language"] = true;
If you want to programmatically vary the pages in the cache by all HTTP header names, use the VaryByUnspecifiedParameters method of the HttpCacheVaryByHeaders class:
HttpCacheVaryByHeaders.VaryByUnspecifiedParameters();
The preceding code is equivalent to using the asterisk with the VaryByHeader attribute.
Vary By Custom Strings
The VaryByCustom attribute in the @OutputCache directive allows you to vary the versions of page output by the value of a custom string. The string you assign to the VaryByCustom attribute simply represents the description of the algorithm employed to vary page outputs. The string is then passed to the GetVaryByCustomString method, if any, in the global.asax file. The method takes the string and returns another string that is specific to the request. Let’s examine a concrete example—varying pages by the type of device that requests the page. You use, say, the string device with the VaryByCustom attribute:
<%@ OutputCache Duration="60" VaryByParam="None" VaryByCustom="device" %>
Next, you add your application-specific GetVaryByCustomString method in the global.asax file. Here’s a possible implementation:
public override string GetVaryByCustomString(HttpContext context, string custom)
{
if (custom == "device")
return context.Request.Browser.Type;
return base.GetVaryByCustomString(context, custom);
}
The output of the page is varied by user agent
string. You can use any other custom information as long as the
information is available through the HttpContext
class. You can’t use information that is known only when the page is
loaded, such as the theme. Custom information gathered by a custom HTTP
module might be used if the HTTP module parks the information in the Items collection of the HttpContext object, and as long as the HTTP module is triggered before the request to resolve the page output cache is made.
Nicely enough, the feature described
above—varying pages by user agent strings—is natively available since
ASP.NET 1.0. The only difference is that it uses the keyword browser instead of device. In other words, the following code is perfectly acceptable and leverages the implementation of GetVaryByCustomString on the base HttpApplication class:
<%@ OutputCache Duration="60" VaryByParam="None" VaryByCustom="browser" %>
You use the SetVaryByCustom method on the HttpCachePolicy class if you don’t like the declarative approach:
Response.Cache.SetVaryByCustom("browser");