|
Using Virtual Application Paths ( ~ ) in Any File!
Earlier today, I posted an article about using virtual application paths inside a css file. While that approach is indeed a step in the right direction, I wasn't quite happy w/ the way I had implemented it - specifically the fact that a new file would be spawned alongside the real css file, with a "_resolved.css" appendage.
This approach only works if you give ASP.NET full access to write, and even then, it's easy for a file to get locked, resulting in a fatty exception.
Jon Gilkison had a great idea to use HttpHandlers to intercept the request & intelligently cache the resolved css in memory. I took his code and slightly modified it to what you see below. This is by far the best approach. Props to Jon for writing the bulk of HttpHandler code.
Once employed, this is what your css links in markup will look like:
 <link rel="stylesheet" href="~/resources/ui/all.css.ashx" />
<link rel="stylesheet" href="~/resources/ui/msie.css.ashx" />
Notice that the only change is the additional ".ashx" applied to the end of the file name. The ".ashx" exists to distinguish this resource from other ".js" files that should not get resolved. No controls to use or register - very clean & mad stupid dope.
This is the FileResolver class - the HttpHandler responsible for intercepting any calls requesting (in this case) .css.ashx files (could be any file - .myExtension.ashx).
Warning: Lots of code below. Copy, build, rock on.
 namespace Oxford.Web
{
public class FileResolver : IHttpHandler
{
internal class FileCacheItem
{
internal string Content;
internal DateTime DateEntered = DateTime.Now;
internal FileCacheItem(string content)
{
this.Content = content;
}
}
private FileCacheItem UpdateFileCache(HttpContext context, string filePath)
{
string content;
using(FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read))
{
using(StreamReader sr = new StreamReader(fs))
{
content = sr.ReadToEnd();
sr.Close();
}
fs.Close();
}
string relAppPath = HttpRuntime.AppDomainAppVirtualPath;
if(!relAppPath.EndsWith("/"))
relAppPath += "/";
content = content.Replace("~/", relAppPath);
FileCacheItem ci = new FileCacheItem(content);
CacheDependency cd = new CacheDependency(filePath);
context.Cache.Insert(filePath, ci, cd);
return ci;
}
public void ProcessRequest(HttpContext context)
{
string absFilePath = context.Request.PhysicalPath.Replace(".ashx", "");
if(absFilePath.IndexOf("~\\") > -1)
absFilePath = absFilePath.Replace("~", "").Replace("\\\\", "\\");
if(!File.Exists(absFilePath))
{
context.Response.StatusCode = 404;
return;
}
FileCacheItem ci = (FileCacheItem)context.Cache[absFilePath];
if(ci != null)
{
if(context.Request.Headers["If-Modified-Since"] != null)
{
try
{
DateTime date = DateTime.Parse(context.Request.Headers["If-Modified-Since"]);
if(ci.DateEntered.ToString() == date.ToString())
{
context.Response.StatusCode = 304;
context.Response.StatusDescription = "Not Modified";
context.Response.End();
return;
}
}
catch(Exception){}
}
else
{
context.Response.AddHeader("If-Modified-Since", ci.DateEntered.ToString());
}
}
else
{
ci = UpdateFileCache(context, absFilePath);
}
context.Response.Cache.SetLastModified(ci.DateEntered);
context.Response.ContentType = "text/" + GetContentType(Path.GetExtension(absFilePath));
context.Response.Write(ci.Content);
context.Response.End();
}
private string GetContentType(string ext)
{
switch(ext.ToLower())
{
case ".css":
return "css";
break;
case ".xml":
return "xml";
break;
case ".js":
return "javascript";
break;
default:
return "plain";
break;
}
}
#region IHttpHandler Members
public bool IsReusable
{
get
{
return true;
}
}
#endregion
}
}
Since we are using HttpHandlers, you will need to add one entry to your web.config file.
 ...
<httpHandlers>
<add verb="GET" path="*.css.ashx" type="Oxford.Web.FileResolver,Oxford.Web" />
</httpHandlers>
...
Another benefit to this approach is that you can use it for any file type, not just css. Simply add additional line entries to your web.config for other types, like .js. I tested with a javascript file and it worked like a charm.
 ...
<httpHandlers>
<add verb="GET" path="*.css.ashx" type="Oxford.Web.FileResolver,Oxford.Web" />
<add verb="GET" path="*.js.ashx" type="Oxford.Web.FileResolver,Oxford.Web" />
</httpHandlers>
...
Likewise, to add the script tag to your markup would look like this:
 <script lang="javascript" src="~/resources/script/script.js.ashx"> </script>
"You take yourself out of the game, you start talking about puppy dogs and ice cream and of course it's going to end up on the friendship tip." - DoubleDown, Swingers
|
New Post Notification
Search Posts
Recent Posts
Lifextender v0.9.3.0 Available for Download
Visual Studio 2008 Add-in Compatibility
Lifextender v0.9.2.9 released
Introducing Lifextender - a commercial remover app for Vista Media Center
Commercial Remover app for Vista Media Center
Escape XML string characters in C#
MRU Cleaner for Orcas Beta 1
Property Manager Updated 1.0.0.4
Localizing existing strings into resource files is teh lame
Dispatch v1.0 Released.... finally.
MRU Cleaner v1.0.0.5 - Now serving your file-cleaning needs too
Explore in Windows Add-In updated - 1.0.0.2
MethodInvoker + Anonymous Methods = tEh r0x0r
Kick-ass Vista Firefox Theme
New Clipboard Manager Upgrade 1.0.0.7
Project MRU Cleaner 1.0.0.4 - Vista Ready and stupid dope
Firebug - My New Favorite Extension
Commercial Skip for Media Center Vista RC2
Dispatch v0.9.0.59 Released
New Remote-Overlay View Feature for Dispatch
New Version of Dispatch Released - v0.9.0.58
Remove Bookmark Custom Site Icons in Firefox 2.0
Dispatch beta 0.9.0.57 Available
About Meeself
People call me Bobby DeRosa
I live somewhere in San Diego, CA
MCSD, MCAD, MCP
|
|