.NET Core MVC/Razor Page中使用TagHelpers的asp-append-version属性就可以为css或者js文件加上版本号。
例如:
<link rel="stylesheet" href="~/css/site.min.css" asp-append-version="true" />
这样输出的Html是这样的:
<link rel="stylesheet" href="/css/site.min.css?v=vSLTk9NafLa61jo5ciF5mRO-U6T-lyeecxK1dVwqCQQ" />
我们可以查看LinkTagHelper的源代码,它是通过FileVersionProvider读取文件,使用SHA256算法计算文件内容的哈希值 ,然后它将对其进行url编码并将其添加到url上。
只有在文件更改时才会重新计算哈希值 ,因为它会添加到缓存中,但具有基于文件观察程序的过期触发器。这里可以看到使用的版本号并不是程序编译后的版本号,而是基于资源文件的内容的。
根据FileVersionProvider的当前实现可以看到,只有url是站内相对路径,才会实现append version,如果是一个远程地址url则不会添加哈希值。
可是在MVC4/5中没有这样的TagHelpers,虽然从MVC4开始推荐使用BundleConfig把资源文件捆绑输出,但是我们可以参考.NET Core asp-append-version的原理自己在ASP.NET MVC4/5中实现这个功能。
我们可以在程序启动的时候读取文件获得哈希值,把它放在缓存里。然后在引用样式或者JS的时候append到url后面。代码如下:
// UrlHelper扩展方法
public static string Version(this UrlHelper me, string path) {
if (string.IsNullOrWhiteSpace(path))
return path;
var hash = GetVersion(path);
if (string.IsNullOrWhiteSpace(hash))
return path;
path = path += "?v=" + HttpUtility.UrlEncode(hash);
return path;
}
// 获取Version,并写入Cache
private static string GetVersion(string path) {
var cache = HttpRuntime.Cache.Get(path);
if (cache != null)
return cache.ToString();
var hash = GetSHA256HashFromString(path);
HttpRuntime.Cache.Insert(path, hash);
return hash;
}
// 获取文件SHA256 hash value
private static string GetSHA256HashFromString(string path)
{
var file = AppDomain.CurrentDomain.BaseDirectory + path;
if (File.Exists(file))
{
var bytes = File.ReadAllBytes(file);
var sha256 = new SHA256CryptoServiceProvider();
var hash = sha256.ComputeHash(bytes);
var sb = new StringBuilder();
for (int i = 0; i < hash.Length; i++)
{
sb.Append(hash[i].ToString("x2"));
}
return sb.ToString();
}
return string.Empty;
}
在视图上这样引用:
@using 扩展方法所在命名空间
<link rel="stylesheet" href="@Url.Version("/Static/Style/main.css")" />
这里只是示例代码,实际使用还要考虑优化一下,比如url本身是带参数的,读取文件失败等情况也要处理一下。
这样只要文件没有修改,他的version就不会变化了。