ASP.net Core MVC項(xiàng)目給js文件添加版本號(hào)
需求:使用ASP.net Core Mvc開發(fā)公司內(nèi)部web系統(tǒng),給視圖中js(css,image也可以)文件添加版本號(hào)避免緩存問題。
解決方法:利用Taghelper提供的標(biāo)簽(asp-append-version)可以實(shí)現(xiàn)
<script src="~/Scripts/Biz/VillageResource/XXXX.js" asp-append-version="true">script>
效果:

備注:刷新頁面js版本號(hào)不會(huì)變化,直到變動(dòng)js內(nèi)容變化,版本號(hào)才會(huì)變化。下文根據(jù)源碼,了解asp-append-version是如何實(shí)現(xiàn)的。
if (AppendVersion == true)
{
EnsureFileVersionProvider();
if (Href != null)
{
var index = output.Attributes.IndexOfName(HrefAttributeName);
var existingAttribute = output.Attributes[index];
output.Attributes[index] = new TagHelperAttribute(
existingAttribute.Name,
FileVersionProvider.AddFileVersionToPath(ViewContext.HttpContext.Request.PathBase, Href),
existingAttribute.ValueStyle);
}
}
private void EnsureFileVersionProvider()
{
if (FileVersionProvider == null)
{
FileVersionProvider = ViewContext.HttpContext.RequestServices.GetRequiredService();
}
}
解析IFileVersionProvider的實(shí)現(xiàn),然后調(diào)用AddFileVersionToPath方法添加版本號(hào),AddFileVersionToPath源碼如下:
public string AddFileVersionToPath(PathString requestPathBase, string path)
{
if (path == null)
{
throw new ArgumentNullException(nameof(path));
}
var resolvedPath = path;
var queryStringOrFragmentStartIndex = path.IndexOfAny(QueryStringAndFragmentTokens);
if (queryStringOrFragmentStartIndex != -1)
{
resolvedPath = path.Substring(0, queryStringOrFragmentStartIndex);
}
if (Uri.TryCreate(resolvedPath, UriKind.Absolute, out var uri) && !uri.IsFile)
{
// Don't append version if the path is absolute.
return path;
}
if (Cache.TryGetValue(path, out string value))
{
return value;
}
var cacheEntryOptions = new MemoryCacheEntryOptions();
cacheEntryOptions.AddExpirationToken(FileProvider.Watch(resolvedPath));
var fileInfo = FileProvider.GetFileInfo(resolvedPath);
if (!fileInfo.Exists &&
requestPathBase.HasValue &&
resolvedPath.StartsWith(requestPathBase.Value, StringComparison.OrdinalIgnoreCase))
{
var requestPathBaseRelativePath = resolvedPath.Substring(requestPathBase.Value.Length);cacheEntryOptions.AddExpirationToken(FileProvider.Watch(requestPathBaseRelativePath));
fileInfo = FileProvider.GetFileInfo(requestPathBaseRelativePath);
}
if (fileInfo.Exists)
{
value = QueryHelpers.AddQueryString(path, VersionKey, GetHashForFile(fileInfo));
}
else
{
// if the file is not in the current server.
value = path;
}
cacheEntryOptions.SetSize(value.Length * sizeof(char));
value = Cache.Set(path, value, cacheEntryOptions);
return value;
}
private static stringGetHashForFile(IFileInfo fileInfo)
{
using (var sha256 = CryptographyAlgorithms.CreateSHA256())
{
using (var readStream = fileInfo.CreateReadStream())
{
var hash = sha256.ComputeHash(readStream);
return WebEncoders.Base64UrlEncode(hash);
}
}
}
通過AddFileVersionToPath源碼可以弄明白:
js版本號(hào) 如何實(shí)現(xiàn)的?
在GetHashForFile方法,根據(jù)文件的內(nèi)容利用SHA256算法得到其hash值,然后通過url編碼得到j(luò)s的版本號(hào)如:?v=b_XmH4_MtWTW4959ESAEqaO3-Tqh9QSlrJgwrQ1YplA
為什么更改了js文件內(nèi)容,版本號(hào)會(huì)改變?
第一次得到版本號(hào),會(huì)放入緩存中( value = Cache.Set(path, value, cacheEntryOptions);),同時(shí)緩存添加過期條件,判斷依據(jù)文件是否發(fā)生變化( cacheEntryOptions.AddExpirationToken(FileProvider.Watch(requestPathBaseRelativePath));),否-直接或從緩存中獲取。是-調(diào)用GetHashForFile方法重新生成。
動(dòng)手添加個(gè)獲取版本號(hào)的擴(kuò)展方法
public static class HttpContextExtends
{
public static string AddFileVersionToPath(this HttpContext context, string path)
{
return context
.RequestServices
.GetRequiredService()
.AddFileVersionToPath(context.Request.PathBase, path);
}
}
view中使用
@section pageScript
{
效果

