In an ASP.NET site, I would like to add an "Expires" header to certain static files, so I added a clientCache
configuration like this for the folder where these file are:
<system.webServer>
<staticContent>
<clientCache cacheControlMode="UseExpires" httpExpires="Wed, 13 Feb 2013 08:00:00 GMT" />
</staticContent>
If possible, I would like to compute the value of httpExpires
programmatically, to set it for example to the time the file was last updated + 24 hours.
Is there a way to configure the cache control to get the value of httpExpires
by calling a method?
If not, what are the alternatives? I thought about writing a custom http handler, but maybe there is a simpler solution...
EDIT: please note that these are static files, so they are not served by the regular asp.net page handler.
You can use Response.Cache
to set caching programmatically.
Here's a nicely looking tutorial.
Basically you want to set the caching policy to Public
(clientside + proxies) and set the expiration header. Some of the methods are named rather awkwardly, but this one is easy enough.
HttpContext.Current.Response.Cache.SetCacheability(HttpCacheability.Public);
HttpContext.Current.Response.Cache.SetExpires(yourCalculatedDateTime);
(ASP.NET designers didn't like the Law of Demeter much, did they?)
Alternatively you can access the heareds via Response.Headers
collection, where you can change them explicitly.
Both these ways are accessible in all ASP.NET handlers (aspx, asmx) and could be changed probably everywhere where you can access the current HttpContext.
Thanks to @HonzaBrestan, who put me on the right track with the HTTP Module idea, I came up with a solution like this one, which I want to share in case it will be useful for someone else.
using System;
using System.Collections.Generic;
using System.IO;
using System.Web;
public class ExpirationModule : IHttpModule {
HttpApplication _context;
#region static initialization for this example - this should be a config section
static Dictionary<string, TimeSpan> ExpirationTimes;
static TimeSpan DefaultExpiration = TimeSpan.FromMinutes(15);
static CrlExpirationModule() {
ExpirationTimes = new Dictionary<string, TimeSpan>();
ExpirationTimes["~/SOMEFOLDER/SOMEFILE.XYZ"] = TimeSpan.Parse("0.0:30");
ExpirationTimes["~/ANOTHERFOLDER/ANOTHERFILE.XYZ"] = TimeSpan.Parse("1.1:00");
}
#endregion
public void Init(HttpApplication context) {
_context = context;
_context.EndRequest += ContextEndRequest;
}
void ContextEndRequest(object sender, EventArgs e) {
// don't use Path as it contains the application name
string requestPath = _context.Request.AppRelativeCurrentExecutionFilePath;
string expirationTimesKey = requestPath.ToUpperInvariant();
if (!ExpirationTimes.ContainsKey(expirationTimesKey)) {
// not a file we manage
return;
}
string physicalPath = _context.Request.PhysicalPath;
if (!File.Exists(physicalPath)) {
// we do nothing and let IIS return a regular 404 response
return;
}
FileInfo fileInfo = new FileInfo(physicalPath);
DateTime expirationTime = fileInfo.LastWriteTimeUtc.Add(ExpirationTimes[expirationTimesKey]);
if (expirationTime <= DateTime.UtcNow) {
expirationTime = DateTime.UtcNow.Add(DefaultExpiration);
}
_context.Response.Cache.SetExpires(expirationTime);
}
public void Dispose() {
}
}
Then you need to add the module in the web config (IIS 7):
<system.webServer>
<modules>
<add name="ExpirationModule" type="ExpirationModule"/>
</modules>
</system.webServer>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With