16

所以我正在阅读这篇关于 CSS/JS 文件的 ASP.NET MVC 中的“自动版本控制”的 stackoverflow 帖子,并且想知道执行此操作的“最佳”策略是什么。

提供的解决方案会插入一个程序集编号 - 这意味着每次发布时 - 它都会更改每个文件,这并不理想,因为如果您只修改 1 个 *.css 或 *.js ,那么它会更改每个文件。

1)如何仅针对“单个文件”而不是使用修改日期或 IIS7 上的其他内容使用站点范围的程序集来完成?

2)另外,如果我有某种“静态”资产,例如 - http://static.domain.com/js/123.js - 如果有人集成了这个静态文件,我如何使用重写来发送请求的最新文件链接到他们的网站?

http://static.domain.com/js/123.js是链接,当有此请求时 - 检查并发送最新文件?

4

6 回答 6

22

ASP.NET 4.5+ 带有一个内置的捆绑和缩小框架 ,旨在解决这个问题。

如果您绝对需要一个简单的自己动手解决方案,您可以使用下面的答案,但我总是说正确的方法是使用捆绑和缩小框架。


您可以像这样修改AssemblyInfo.cs文件:

Change
[assembly: AssemblyVersion("1.0.0.0")]
to    
[assembly: AssemblyVersion("1.0.*")]

这意味着每次构建项目时,都会有一个高于前一个的新程序集版本。现在您有了唯一的版本号。

创建一个 UrlHelperExtension 类,该类将在视图中需要时帮助获取此信息:

public static class UrlHelperExtensions
{
    public static string ContentVersioned(this UrlHelper self, string contentPath)
    {
        string versionedContentPath = contentPath + "?v=" + Assembly.GetAssembly(typeof(UrlHelperExtensions)).GetName().Version.ToString();
        return self.Content(versionedContentPath);
    }
}

您现在可以通过以下方式轻松地将版本号添加到您的视图中:

<link href="@Url.ContentVersioned("style.css")" rel="stylesheet" type="text/css" />

查看您的页面源时,您现在将看到如下所示的内容

<link href="style.css?v=1.0.4809.30029" rel="stylesheet" type="text/css" />
于 2013-03-02T17:08:49.877 回答
9

更新:以前的版本在 Azure 上不起作用,我在下面进行了简化和更正。(注意,要在 IIS Express 的开发模式下工作,您需要从 Microsoft http://www.iis.net/downloads/microsoft/url-rewrite安装 URL Rewrite 2.0 - 它使用 WebPi 安装程序,确保首先关闭 Visual Studio)

如果您想更改文件的实际名称,而不是附加查询字符串(某些代理/浏览器会忽略静态文件)您可以按照以下步骤操作:(我知道这是一篇旧帖子,但我跑了在开发解决方案时跨越它:

怎么做: 每次构建项目时自动增加程序集版本,并将该数字用于您希望保持刷新的特定资源的路由静态文件。(所以 something.js 包含在 something.v1234.js 中,每次构建项目时 1234 都会自动更改) - 我还添加了一些额外的功能,以确保在生产中使用 .min.js 文件并使用 regular.js 文件调试时(我正在使用 WebGrease 来自动化缩小过程)这个解决方案的一个好处是它可以在本地/开发模式以及生产模式下工作。(我使用的是 Visual Studio 2015 / Net 4.6,但我相信这也适用于早期版本。

第 1 步:在构建时启用程序集的自动增量在 AssemblyInfo.cs 文件中(在项目的“属性”部分下找到,更改以下行:

[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

[assembly: AssemblyVersion("1.0.*")]
//[assembly: AssemblyFileVersion("1.0.0.0")]

第 2 步:在 web.config 中为具有嵌入式版本 slug 的文件设置 url 重写(参见第 3 步)

在 web.config(项目的主要部分)中,在 <system.webServer>我将其直接放在</httpProtocol>结束标记之后的部分中添加以下规则。

<rewrite>
  <rules>
    <rule name="static-autoversion">
      <match url="^(.*)([.]v[0-9]+)([.](js|css))$" />
      <action type="Rewrite" url="{R:1}{R:3}" />
    </rule>
    <rule name="static-autoversion-min">
      <match url="^(.*)([.]v[0-9]+)([.]min[.](js|css))$" />
      <action type="Rewrite" url="{R:1}{R:3}" />
    </rule>
  </rules>
</rewrite>

第 3 步:设置应用程序变量以读取您当前的程序集版本并在您的 js 和 css 文件中创建版本 slug。

在 Global.asax.cs(在项目的根目录中找到)中将以下代码添加到受保护的 void Application_Start() (在注册行之后)

            // setup application variables to write versions in razor (including .min extension when not debugging)
            string addMin = ".min";
            if (System.Diagnostics.Debugger.IsAttached) { addMin = ""; }  // don't use minified files when executing locally
            Application["JSVer"] = "v" + System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString().Replace('.','0') + addMin + ".js";
            Application["CSSVer"] = "v" + System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString().Replace('.', '0') + addMin + ".css";

第 4 步:使用我们在 Global.asax.cs 中设置的应用程序变量更改 Razor 视图中的 src 链接

@HttpContext.Current.Application["CSSVer"]
@HttpContext.Current.Application["JSVer"]

例如,在我的 _Layout.cshtml 中,在我的 head 部分中,我有以下样式表代码块:

<!-- Load all stylesheets -->
<link rel='stylesheet' href='https://fontastic.s3.amazonaws.com/8NNKTYdfdJLQS3D4kHqhLT/icons.css' />
<link rel='stylesheet' href='/Content/css/main-small.@HttpContext.Current.Application["CSSVer"]' />
<link rel='stylesheet' media='(min-width: 700px)' href='/Content/css/medium.@HttpContext.Current.Application["CSSVer"]' />
<link rel='stylesheet' media='(min-width: 700px)' href='/Content/css/large.@HttpContext.Current.Application["CSSVer"]' />
@RenderSection("PageCSS", required: false)

这里需要注意几件事:1)文件上没有扩展名。2)也没有.min。这两个都由 Global.asax.cs 中的代码处理

同样,(也在 _Layout.cs 中)在我的 javascript 部分:我有以下代码:

<script src="~/Scripts/all3bnd100.min.js" type="text/javascript"></script>
<script src="~/Scripts/ui.@HttpContext.Current.Application["JSVer"]" type="text/javascript"></script>
@RenderSection("scripts", required: false)

第一个文件是我使用 WebGrease 手动创建的所有第 3 方库的捆绑包。如果我添加或更改捆绑包中的任何文件(这种情况很少见),然后我手动将文件重命名为 all3bnd101.min.js、all3bnd102.min.js 等...此文件与重写处理程序不匹配,所以将一直缓存在客户端浏览器上,直到您手动重新捆绑/更改名称。

第二个文件是 ui.js(将被写为 ui.v12345123.js 或 ui.v12345123.min.js 取决于您是否在调试模式下运行)这将被处理/重写。(你可以在 Global.asax.cs 的 Application_OnBeginRequest 中设置一个断点来观察它的工作)

对此的完整讨论:在 ASP.NET MVC 5 中简化 Javascript/CSS 的自动版本控制以停止缓存问题(在 Azure 和本地工作)有或没有 URL 重写 (包括一种没有 URL 重写的方法)

于 2015-12-08T07:48:41.117 回答
6

1) 改用文件修改时间。这是一个例子:

public static string GeneratePathWithTime(string cssFileName)
{
  var serverFilePath = server.MapPath("~/static/" + cssFileName);
  var version = File.GetLastWriteTime(serverFilePath).ToString("yyyyMMddhhmmss");
  return string.Format("/static/{0}/{1}", version, cssFileName);
}

这将为“”生成类似“ /static/201109231100/style.css”的路径style.css(假设您style.css位于static目录中)。然后,您将在 IIS 中添加一个重写规则,将“ /static/201109231100/style.css”重写为“ /static/style.css”。版本号只有在css文件被修改后才会改变,并且只适用于修改后的文件。

2)您可以通过HttpModule处理对123.js的请求并发送它的最新内容,但我认为您不能保证请求获得最新版本。这取决于浏览器如何处理其缓存。您可以在响应标头中设置更早的过期时间(例如一分钟前),以告诉浏览器始终重新下载文件,但这完全取决于浏览器本身来决定是否重新下载文件. 这就是为什么每次我们更新您的问题 1) 中的文件时,我们都需要为修改后的文件生成不同的路径,如果以前从未访问过该 URL,浏览器将始终尝试下载该文件。

于 2011-09-23T04:14:05.440 回答
4

我写了一个为我做 CacheBusting 的 Url Helper。

public static string CacheBustedContent(this UrlHelper helper, string contentPath)
{
    var path = string.Empty;

    if (helper.RequestContext.HttpContext.Cache["static-resource-" + contentPath] == null)
    {
        var fullpath = helper.RequestContext.HttpContext.Server.MapPath(contentPath);
        var md5 = GetMD5HashFromFile(fullpath);
        path = helper.Content(contentPath) + "?v=" + md5;

        helper.RequestContext.HttpContext.Cache.Add("static-resource-" + contentPath, path, null, System.Web.Caching.Cache.NoAbsoluteExpiration, new TimeSpan(24, 0, 0), System.Web.Caching.CacheItemPriority.Default, null);
    }
    else
    {
        path = helper.RequestContext.HttpContext.Cache["static-resource-" + contentPath].ToString();
    }

    return path;
}

您可以将 GetMD5HashFromFile() 替换为 CRC 或任何其他类型的调用,该调用会根据文件的内容或最后修改日期生成唯一字符串。

缺点是每当缓存失效时都会调用它。如果您以某种方式实时更改文件,但不重置应用程序池,您可能需要触摸 web.config 以使其正确重新加载。

于 2012-08-14T18:29:29.593 回答
3

您可能想看看 Dean Hume 的 Blogpost MVC 和 HTML5 Application Cache。在那篇文章中,他指出了一种优雅的方式来自动处理每个请求的版本控制,使用@ShirtlessKirk的类库:

@Url.Content("~/Content/Site.css").AppendHash(Request)
于 2011-09-23T21:49:50.387 回答
2

这个问题现在真的很老了,但如果有人偶然发现它,据我所知,这是当前的技术状态:

  1. 在 ASP.NET Core 中,您可以使用 TagHelpers 并简单地将asp-append-version属性添加到任何<link><script>标记:

    <script src="~/js/my.js" asp-append-version="true"></script>
    
  2. 对于 ASP.NET Core 和 Framework,都有一个名为WebOptimizer ( https://github.com/ligershark/WebOptimizer ) 的 NuGet 包。它允许捆绑和缩小,并且还将基于内容的版本字符串附加到您的文件中。

  3. 如果你想自己做,有一个方便的IFileVersionProvider界面,你可以从你IServiceProvider的 .NET Core 中获得:

    // this example assumes, you at least have a HttpContext
    var fileVersionProvider = httpContext.RequestServices.GetRequiredService<IFileVersionProvider>();
    string path = httpContext.Content("/css/site.css");
    string pathWithVersionString = fileVersionProvider.AddFileVersionToPath(httpContext.Request.PathBase, path);
    

    对于 .NET Framework,您可以FileVersionProvider从此处获取源代码:https ://github.com/dotnet/aspnetcore/blob/main/src/Mvc/Mvc.Razor/src/Infrastructure/DefaultFileVersionProvider.cs 您将不得不做一些工作,就像用MemoryCache.Defaultor a或其他东西替换缓存ConcurrentDictionary,但“肉”就在那里。

于 2021-08-30T23:23:20.733 回答