0

MVC 4 捆绑包是否解决了陈旧 .js 文件的问题?这些 .js 文件缓存在客户端的计算机上,因此它们有时不会随新部署进行更新。

捆绑并让框架确定 etag 是否匹配可以解决 MVC 4 中的问题?

同样,使用 MVC 3 时有哪些替代方案?

4

3 回答 3

4

MVC 4 捆绑发出捆绑资源的散列。

例如。

<link href="@System.Web.Optimization.BundleTable.
    Bundles.ResolveBundleUrl("~/Content/css")"
    rel="stylesheet"
    type="text/css" />

结果如下:

 <link href="/Content/css?v=ji3nO1pdg6VLv3CVUWntxgZNf1z"
     rel="stylesheet" type="text/css" />

如果文件更改,v参数将更改,强制客户端重新下载资源。

来源:http ://bit.ly/xT8ZM5

于 2012-11-17T17:46:20.027 回答
1

您可以将它与 MVC 3 一起使用。它位于System.Web.Optimization.dll. 您可以下载并使用 .

欲了解更多信息: http: //nuget.org/packages/microsoft.web.optimization

例如,在您的 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.
于 2012-11-16T15:53:43.453 回答
1

我最近遇到了脚本缓存的问题。新浏览器(尤其是 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));
于 2012-11-16T16:23:03.693 回答