陈旧的javascript缓存文件和MVC4
MVC4捆绑解决了陈旧的.js文件的问题吗?这些.js文件缓存在客户端计算机上,因此它们有时不会随着新部署而更新 在MVC4中,捆绑并让框架判断etag是否匹配可以解决问题吗陈旧的javascript缓存文件和MVC4,javascript,asp.net-mvc-3,asp.net-mvc-4,Javascript,Asp.net Mvc 3,Asp.net Mvc 4,MVC4捆绑解决了陈旧的.js文件的问题吗?这些.js文件缓存在客户端计算机上,因此它们有时不会随着新部署而更新 在MVC4中,捆绑并让框架判断etag是否匹配可以解决问题吗 类似地,使用MVC 3时有哪些替代方案?您可以将其与MVC 3一起使用。它位于System.Web.Optimization.dll下。你可以下载并使用它 有关更多信息: 例如,在global.asax中,添加以下内容: bundles.Add(new ScriptBundle("~/bundles/jquery").In
类似地,使用MVC 3时有哪些替代方案?您可以将其与MVC 3一起使用。它位于
System.Web.Optimization.dll
下。你可以下载并使用它
有关更多信息:
例如,在global.asax中,添加以下内容:
bundles.Add(new ScriptBundle("~/bundles/jquery").Include(
"~/Scripts/jquery-{version}.js"));
bundles.Add(new ScriptBundle("~/bundles/jqueryui").Include(
"~/Scripts/jquery-ui-{version}.js"));
bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include(
"~/Scripts/jquery.unobtrusive*",
"~/Scripts/jquery.validate*"));
bundles.Add(new ScriptBundle("~/bundles/customjs").Include(
"~/Scripts/jquery.custom.js"));
// or what you want to add different js files.
您可以将其与MVC3一起使用。它位于
System.Web.Optimization.dll
下。你可以下载并使用它
有关更多信息:
例如,在global.asax中,添加以下内容:
bundles.Add(new ScriptBundle("~/bundles/jquery").Include(
"~/Scripts/jquery-{version}.js"));
bundles.Add(new ScriptBundle("~/bundles/jqueryui").Include(
"~/Scripts/jquery-ui-{version}.js"));
bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include(
"~/Scripts/jquery.unobtrusive*",
"~/Scripts/jquery.validate*"));
bundles.Add(new ScriptBundle("~/bundles/customjs").Include(
"~/Scripts/jquery.custom.js"));
// or what you want to add different js files.
我最近遇到了脚本缓存的问题。新浏览器(尤其是Chrome)正在缓存脚本,有时它们不会向服务器发送请求以检查是否有新版本 在MVC3应用程序中,我决定使用自定义路由处理程序来处理它。在html中,我将修订附加到每个脚本链接。然后在我的处理程序中,我从url中去掉修订号,然后在服务器上搜索实际文件(例如Path/Script.rev1000.js指向Path/Script.js) 这是我的密码:
public class ContentRouteHandler : IRouteHandler
{
private OzirRouteProvider _routeProvider;
public ContentRouteHandler(OzirRouteProvider routeProvider)
{
this._routeProvider = routeProvider;
}
public IHttpHandler GetHttpHandler(RequestContext requestContext)
{
return new ContentHttpHandler(this._routeProvider, this, requestContext);
}
}
internal class ContentHttpHandler : IHttpHandler, IRequiresSessionState
{
private OzirRouteProvider _routeProvider;
private ContentRouteHandler _routeHandler;
private RequestContext _requestContext;
public bool IsReusable { get { return false; } }
public ContentHttpHandler(OzirRouteProvider routeProvider, ContentRouteHandler routeHandler, RequestContext requestContext)
{
this._routeProvider = routeProvider;
this._routeHandler = routeHandler;
this._requestContext = requestContext;
}
public void ProcessRequest(HttpContext context)
{
string contentPath = context.Request.PhysicalPath;
string fileName = Path.GetFileNameWithoutExtension(contentPath);
string extension = Path.GetExtension(contentPath);
string path = Path.GetDirectoryName(contentPath);
bool minify = false;
// Here i get fileName like Script.rev1000.min.js
// I strip revision and .min from it so I'll have Script.js
var match = Regex.Match(fileName, "(\\.rev\\d+)?(\\.min)?$");
if (match.Groups[2].Success)
{
minify = true;
fileName = fileName.Remove(match.Groups[2].Index, match.Groups[2].Length);
contentPath = Path.Combine(path, fileName + extension);
}
if (match.Groups[1].Success)
{
fileName = fileName.Remove(match.Groups[1].Index, match.Groups[1].Length);
contentPath = Path.Combine(path, fileName + extension);
}
if (!File.Exists(contentPath)) // 404
{
throw new HttpException(404, "Not found");
}
DateTime lastModified = this.GetModificationDate(contentPath);
string eTag = this.GetETag(context.Request.RawUrl, contentPath, lastModified);
// Check for modification
string requestETag = context.Request.Headers["If-None-Match"];
string requestLastModified = context.Request.Headers["If-Modified-Since"];
DateTime? requestLastModifiedDate = requestLastModified == null ? null : (DateTime?)DateTime.Parse(requestLastModified).ToUniversalTime().TruncMiliseconds();
// Compare e-tag and modification date
if ((requestLastModified != null || requestETag != null) &&
(requestLastModified == null || requestLastModifiedDate == lastModified) &&
(requestETag == null || requestETag == eTag))
{
context.Response.StatusCode = 304;
context.Response.SuppressContent = true;
context.Response.Flush();
return;
}
switch (extension)
{
case ".js":
context.Response.ContentType = "application/x-javascript";
if (minify) // minify file?
{
string minContentPath = Path.Combine(path, fileName + ".min" + extension);
this.MinifyJs(contentPath, minContentPath);
contentPath = minContentPath;
}
break;
default:
throw new NotSupportedException(string.Format("Extension {0} is not supported yet", extension));
}
// g-zip and deflate support
string acceptEncoding = context.Request.Headers["Accept-Encoding"];
if (!string.IsNullOrEmpty(acceptEncoding) && acceptEncoding.Contains("gzip"))
{
context.Response.Filter = new System.IO.Compression.GZipStream(context.Response.Filter, System.IO.Compression.CompressionMode.Compress);
context.Response.AppendHeader("Content-Encoding", "gzip");
}
else if (!string.IsNullOrEmpty(acceptEncoding) && acceptEncoding.Contains("deflate"))
{
context.Response.Filter = new System.IO.Compression.DeflateStream(context.Response.Filter, System.IO.Compression.CompressionMode.Compress);
context.Response.AppendHeader("Content-Encoding", "deflate");
}
context.Response.AddCacheDependency(new CacheDependency(contentPath));
context.Response.AddFileDependency(contentPath);
context.Response.Cache.SetCacheability(HttpCacheability.ServerAndPrivate);
context.Response.Cache.SetETag(eTag);
context.Response.Cache.SetExpires(DateTime.Now.AddDays(7));
context.Response.Cache.SetLastModified(lastModified);
context.Response.Cache.SetMaxAge(TimeSpan.FromDays(7));
context.Response.TransmitFile(contentPath);
context.Response.Flush();
}
private void MinifyJs(string contentPath, string minContentPath)
{
this._log.DebugFormat("Minifying JS {0} into {1}", contentPath, minContentPath);
if (!File.Exists(minContentPath) || File.GetLastWriteTime(contentPath) > File.GetLastWriteTime(minContentPath))
{
string content = File.ReadAllText(contentPath, Encoding.UTF8);
JavaScriptCompressor compressor = new JavaScriptCompressor();
compressor.Encoding = Encoding.UTF8;
compressor.ErrorReporter = new CustomErrorReporter(LoggingType.Debug);
content = compressor.Compress(content);
File.WriteAllText(minContentPath, content, Encoding.UTF8);
}
}
private DateTime GetModificationDate(string contentPath)
{
DateTime lastModified = File.GetLastWriteTimeUtc(contentPath).TruncMiliseconds();
return lastModified;
}
private string GetETag(string url, string contentPath, DateTime lastModified)
{
string eTag = string.Format("url={0},path={1},lm={2},rev={3}", url, contentPath, lastModified, AppInfo.Revision);
return Quote(GetHash(eTag));
}
private static string GetHash(string value)
{
byte[] data = MD5.Create().ComputeHash(Encoding.UTF8.GetBytes(value));
StringBuilder hex = new StringBuilder(data.Length * 2);
foreach (byte b in data)
{
hex.AppendFormat("{0:x2}", b);
}
return hex.ToString();
}
private static string Quote(string value)
{
return string.Format("\"{0}\"", value);
}
}
要使用它,您必须打开RouteExistingFiles
,并注册路由,例如:
routes.Add(new Route("Content/{*resource}", new RouteValueDictionary(), new RouteValueDictionary { { "resource", @".*(\.css)$" } }, contentHandler));
routes.Add(new Route("Scripts/{*resource}", new RouteValueDictionary(), new RouteValueDictionary { { "resource", @".*(\.js)$" } }, contentHandler));
我最近遇到了脚本缓存的问题。新浏览器(尤其是Chrome)正在缓存脚本,有时它们不会向服务器发送请求以检查是否有新版本 在MVC3应用程序中,我决定使用自定义路由处理程序来处理它。在html中,我将修订附加到每个脚本链接。然后在我的处理程序中,我从url中去掉修订号,然后在服务器上搜索实际文件(例如Path/Script.rev1000.js指向Path/Script.js) 这是我的密码:
public class ContentRouteHandler : IRouteHandler
{
private OzirRouteProvider _routeProvider;
public ContentRouteHandler(OzirRouteProvider routeProvider)
{
this._routeProvider = routeProvider;
}
public IHttpHandler GetHttpHandler(RequestContext requestContext)
{
return new ContentHttpHandler(this._routeProvider, this, requestContext);
}
}
internal class ContentHttpHandler : IHttpHandler, IRequiresSessionState
{
private OzirRouteProvider _routeProvider;
private ContentRouteHandler _routeHandler;
private RequestContext _requestContext;
public bool IsReusable { get { return false; } }
public ContentHttpHandler(OzirRouteProvider routeProvider, ContentRouteHandler routeHandler, RequestContext requestContext)
{
this._routeProvider = routeProvider;
this._routeHandler = routeHandler;
this._requestContext = requestContext;
}
public void ProcessRequest(HttpContext context)
{
string contentPath = context.Request.PhysicalPath;
string fileName = Path.GetFileNameWithoutExtension(contentPath);
string extension = Path.GetExtension(contentPath);
string path = Path.GetDirectoryName(contentPath);
bool minify = false;
// Here i get fileName like Script.rev1000.min.js
// I strip revision and .min from it so I'll have Script.js
var match = Regex.Match(fileName, "(\\.rev\\d+)?(\\.min)?$");
if (match.Groups[2].Success)
{
minify = true;
fileName = fileName.Remove(match.Groups[2].Index, match.Groups[2].Length);
contentPath = Path.Combine(path, fileName + extension);
}
if (match.Groups[1].Success)
{
fileName = fileName.Remove(match.Groups[1].Index, match.Groups[1].Length);
contentPath = Path.Combine(path, fileName + extension);
}
if (!File.Exists(contentPath)) // 404
{
throw new HttpException(404, "Not found");
}
DateTime lastModified = this.GetModificationDate(contentPath);
string eTag = this.GetETag(context.Request.RawUrl, contentPath, lastModified);
// Check for modification
string requestETag = context.Request.Headers["If-None-Match"];
string requestLastModified = context.Request.Headers["If-Modified-Since"];
DateTime? requestLastModifiedDate = requestLastModified == null ? null : (DateTime?)DateTime.Parse(requestLastModified).ToUniversalTime().TruncMiliseconds();
// Compare e-tag and modification date
if ((requestLastModified != null || requestETag != null) &&
(requestLastModified == null || requestLastModifiedDate == lastModified) &&
(requestETag == null || requestETag == eTag))
{
context.Response.StatusCode = 304;
context.Response.SuppressContent = true;
context.Response.Flush();
return;
}
switch (extension)
{
case ".js":
context.Response.ContentType = "application/x-javascript";
if (minify) // minify file?
{
string minContentPath = Path.Combine(path, fileName + ".min" + extension);
this.MinifyJs(contentPath, minContentPath);
contentPath = minContentPath;
}
break;
default:
throw new NotSupportedException(string.Format("Extension {0} is not supported yet", extension));
}
// g-zip and deflate support
string acceptEncoding = context.Request.Headers["Accept-Encoding"];
if (!string.IsNullOrEmpty(acceptEncoding) && acceptEncoding.Contains("gzip"))
{
context.Response.Filter = new System.IO.Compression.GZipStream(context.Response.Filter, System.IO.Compression.CompressionMode.Compress);
context.Response.AppendHeader("Content-Encoding", "gzip");
}
else if (!string.IsNullOrEmpty(acceptEncoding) && acceptEncoding.Contains("deflate"))
{
context.Response.Filter = new System.IO.Compression.DeflateStream(context.Response.Filter, System.IO.Compression.CompressionMode.Compress);
context.Response.AppendHeader("Content-Encoding", "deflate");
}
context.Response.AddCacheDependency(new CacheDependency(contentPath));
context.Response.AddFileDependency(contentPath);
context.Response.Cache.SetCacheability(HttpCacheability.ServerAndPrivate);
context.Response.Cache.SetETag(eTag);
context.Response.Cache.SetExpires(DateTime.Now.AddDays(7));
context.Response.Cache.SetLastModified(lastModified);
context.Response.Cache.SetMaxAge(TimeSpan.FromDays(7));
context.Response.TransmitFile(contentPath);
context.Response.Flush();
}
private void MinifyJs(string contentPath, string minContentPath)
{
this._log.DebugFormat("Minifying JS {0} into {1}", contentPath, minContentPath);
if (!File.Exists(minContentPath) || File.GetLastWriteTime(contentPath) > File.GetLastWriteTime(minContentPath))
{
string content = File.ReadAllText(contentPath, Encoding.UTF8);
JavaScriptCompressor compressor = new JavaScriptCompressor();
compressor.Encoding = Encoding.UTF8;
compressor.ErrorReporter = new CustomErrorReporter(LoggingType.Debug);
content = compressor.Compress(content);
File.WriteAllText(minContentPath, content, Encoding.UTF8);
}
}
private DateTime GetModificationDate(string contentPath)
{
DateTime lastModified = File.GetLastWriteTimeUtc(contentPath).TruncMiliseconds();
return lastModified;
}
private string GetETag(string url, string contentPath, DateTime lastModified)
{
string eTag = string.Format("url={0},path={1},lm={2},rev={3}", url, contentPath, lastModified, AppInfo.Revision);
return Quote(GetHash(eTag));
}
private static string GetHash(string value)
{
byte[] data = MD5.Create().ComputeHash(Encoding.UTF8.GetBytes(value));
StringBuilder hex = new StringBuilder(data.Length * 2);
foreach (byte b in data)
{
hex.AppendFormat("{0:x2}", b);
}
return hex.ToString();
}
private static string Quote(string value)
{
return string.Format("\"{0}\"", value);
}
}
要使用它,您必须打开RouteExistingFiles
,并注册路由,例如:
routes.Add(new Route("Content/{*resource}", new RouteValueDictionary(), new RouteValueDictionary { { "resource", @".*(\.css)$" } }, contentHandler));
routes.Add(new Route("Scripts/{*resource}", new RouteValueDictionary(), new RouteValueDictionary { { "resource", @".*(\.js)$" } }, contentHandler));
MVC4绑定发出绑定资源的散列 例如
结果如下:
<link href="/Content/css?v=ji3nO1pdg6VLv3CVUWntxgZNf1z"
rel="stylesheet" type="text/css" />
如果文件更改,v
参数将更改,迫使客户端重新下载资源
来源:MVC4绑定会发出绑定资源的散列 例如
结果如下:
<link href="/Content/css?v=ji3nO1pdg6VLv3CVUWntxgZNf1z"
rel="stylesheet" type="text/css" />
如果文件更改,v
参数将更改,迫使客户端重新下载资源
来源:它是否真的解决了即使在服务器上更改了文件,客户端也无法获取最新的.js文件的问题?谢谢它是否真的解决了即使服务器上的文件发生了更改,客户端也无法获取最新的.js文件的问题?谢谢