A problem you can often run into, is that the client keeps the old version cached, which can break things in really weird ways.
This is a simple way to fix this - all you need to do is replace your script and link tags with this usercontrol.
That way, it ensures the url to the files gets a unique querystring added, which makes sure the client refreshes the file if it gets changed.
To do this, create a new class in a C# project, and call it HashedResourceFile, and paste the following code into it:
public class HashedResourceFile : WebControl
{
public Uri ResourceUrl { get; set; }
private string FinalUrl
{
get
{
string url = ResourceUrl.ToString();
// Handle external URL's with not-defined protocol.
if (url.StartsWith("//"))
{
return url;
}
// If the URL does not start with leading slash, it is external, and we don't do anything with it.
if (!url.StartsWith("/"))
{
return url;
}
FileInfo fileInfo = new FileInfo(HttpContext.Current.Server.MapPath(url));
if (!fileInfo.Exists)
{
throw new FileNotFoundException("File in resource not found: " + ResourceUrl);
}
string hash;
using (MD5 md5 = MD5.Create())
{
using (FileStream stream = fileInfo.OpenRead())
{
hash = BitConverter.ToString(md5.ComputeHash(stream)).Replace("-", string.Empty);
}
}
return ResourceUrl + "?filehash=" + hash.Substring(0, 8);
}
}
private ResourceLinkType LinkType
{
get
{
if (ResourceUrl.ToString().EndsWith(".css", StringComparison.InvariantCultureIgnoreCase))
{
return ResourceLinkType.Css;
}
if (ResourceUrl.ToString().EndsWith(".js", StringComparison.InvariantCultureIgnoreCase))
{
return ResourceLinkType.Javascript;
}
return ResourceLinkType.Invalid;
}
}
protected override void Render(HtmlTextWriter writer)
{
if (writer == null)
{
throw new ArgumentNullException("writer", "writer must not be null");
}
if (ResourceUrl == null || string.IsNullOrEmpty(ResourceUrl.ToString()))
{
return;
}
switch (LinkType)
{
case ResourceLinkType.Css:
writer.Write("<link rel=\"stylesheet\" type=\"text/css\" href=\"{0}\" ", FinalUrl);
foreach (string key in Attributes.Keys.Cast<string>().Where(key => key != "rel" && key != "type" && key != "href"))
{
writer.Write("{0}=\"{1}\" ", key, Attributes[key]);
}
writer.Write("/>");
break;
case ResourceLinkType.Javascript:
writer.Write("<script type=\"text/javascript\" src=\"{0}\" ", FinalUrl);
foreach (string key in Attributes.Keys.Cast<string>().Where(key => key != "type" && key != "src"))
{
writer.Write("{0}=\"{1}\" ", key, Attributes[key]);
}
writer.Write("></script>");
break;
default:
return;
}
}
private enum ResourceLinkType
{
Invalid,
Css,
Javascript
}
}
Then, when you create your project, add this to the top of the aspx/ascx file:
<%@ Register TagPrefix="test" Namespace="YourNamespace" Assembly="YourDLL" %>
Then, add this instead of your css/js file references:
<test:HashedResourceFile ResourceUrl="/static/css/style.css" runat="server" />
When the page is rendered, it will add a querystring to the file, containing a hash of the file, that changes when the file changes.
Really simple solution, to a really annoying problem :-)
Ingen kommentarer:
Send en kommentar