ASP.NET MVC 中用于 CSS / JS 文件的自动版本控制?

Posted

技术标签:

【中文标题】ASP.NET MVC 中用于 CSS / JS 文件的自动版本控制?【英文标题】:Auto-versioning in ASP.NET MVC for CSS / JS Files? 【发布时间】:2011-06-18 00:10:28 【问题描述】:

我已经阅读了很多关于如何对 CSS/JS 文件进行自动版本控制的文章 - 但这些文章都没有真正提供在 ASP.NET MVC 中执行此操作的优雅方法。

这个链接 - How to force browser to reload cached CSS/JS files? - 为 Apache 提供了一个解决方案 - 但我有点困惑如何通过 ASP.NET MVC 来实现它?

谁能提供一些建议如何在 IIS7 和 ASP.NET MVC 上执行此操作 - 以便 CSS/JS 文件自动在 URL 中插入一个版本号而不更改文件的位置?

也就是说,链接出来的链接大概是使用 URL Rewrite 或?

<link rel="stylesheet" href="/css/structure.1194900443.css" type="text/css" />
<script type="text/javascript" src="/scripts/prototype.1197993206.js"></script>

谢谢

【问题讨论】:

【参考方案1】:

当遇到这个问题时,我围绕UrlHelperContent 方法编写了一系列包装函数:

编辑:

根据下面 cmets 中的讨论,我更新了这段代码:

public static class UrlHelperExtensions

    private readonly static string _version = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString();

    private static string GetAssetsRoot()
    
        string root = ConfigurationManager.AppSettings["AssetsRoot"];
        return root.IsNullOrEmpty() ? "~" : root;
    

    public static string Image(this UrlHelper helper, string fileName)
    
        return helper.Content(string.Format("0/v2/assets/img/1", GetAssetsRoot(), fileName, _version));
    

    public static string Asset(this UrlHelper helper, string fileName)
    
        return helper.Content(string.Format("0/v2/assets/1", GetAssetsRoot(), fileName, _version));
    

    public static string Stylesheet(this UrlHelper helper, string fileName)
    
        return helper.Content(string.Format("0/v2/assets/css/1", GetAssetsRoot(), fileName, _version));
    

    public static string Script(this UrlHelper helper, string fileName)
    
        return helper.Content(string.Format("0/v2/assets/js/1", GetAssetsRoot(), fileName, _version));
    

将这些函数与以下rewrite 规则结合使用应该可以工作:

<rewrite>
  <rules>
    <rule name="Rewrite assets">
      <match url="^v(.*?)/assets/(.*?)" />
      <action type="Rewrite" url="/assets/R:2" />
    </rule>
  </rules>
</rewrite>

This article 讨论如何在 IIS7 上创建重写规则。

此代码使用当前程序集的版本号作为它发出的文件路径上的查询字符串参数。当我对站点进行更新并且内部版本号增加时,文件上的查询字符串参数也会增加,因此用户代理将重新下载文件。

【讨论】:

嘿clonked - 非常感谢代码但是使用版本号的问题在上面链接的文章中有详细说明。它将阻止缓存,因为带有 GET 参数的请求可能不会被缓存(根据 HTTP 规范),并且 IE 因忽略版本号而臭名昭著 - 这就是为什么我想要一种在 URL /file.123 中包含版本号的方法。 css - ala URL 重写 抱歉,我没有阅读您的链接内容。我很高兴我参加了,因为我知道我的方法是不正确的!我会看看我是否可以修改我的解决方案并分享它。 我已经更新了我发布的代码以使用 IIS 7 的 URL 重写功能。 哦,太棒了,非常感谢 :) 所以你正在通过weblogs.asp.net/scottgu/archive/2010/04/20/… 使用重写工具包? 是的,我发布的重写规则应该适用于微软的 IIS7 重写模块。【参考方案2】:

我通常会在我的资源文件中附加一个虚假的查询字符串..即

<link rel="stylesheet" href="/css/structure.css?v=1194900443" type="text/css" />
<script type="text/javascript" src="/scripts/prototype.js?v=1197993206"></script>

它不需要任何 url 帮助程序,无论后台运行什么都可以工作。老实说,我还没有彻底测试过这种方法,但我发现它总能解决人们遇到的任何资源缓存问题。

您可能必须手动更新v=,但将版本参数从某个配置文件附加到资源中并不难。

编辑:

我回过头来仔细阅读了上面链接的内容,发现您可能已经放弃了这种方法。抱歉再次提出建议。

【讨论】:

当我在 Firefox 和 Chrome 中测试时,这完全阻止了缓存。【参考方案3】:

我想下一个解决方案带有高级选项(调试/发布模式、版本):

通过这种方式包含的Js或Css文件:

<script type="text/javascript" src="Scripts/exampleScript<%=Global.JsPostfix%>" />
<link rel="stylesheet" type="text/css" href="Css/exampleCss<%=Global.CssPostfix%>" />

Global.JsPostfix 和 Global.CssPostfix 在 Global.asax 中的计算方式如下:

protected void Application_Start(object sender, EventArgs e)

    ...
    string jsVersion = ConfigurationManager.AppSettings["JsVersion"];
    bool updateEveryAppStart = Convert.ToBoolean(ConfigurationManager.AppSettings["UpdateJsEveryAppStart"]);
    int buildNumber = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.Revision;
    JsPostfix = "";
#if !DEBUG
    JsPostfix += ".min";
#endif      
    JsPostfix += ".js?" + jsVersion + "_" + buildNumber;
    if (updateEveryAppStart)
    
        Random rand = new Random();
        JsPosfix += "_" + rand.Next();
    
    ...

【讨论】:

我不认为您可以在具有 runat="server" 属性的 head 标记中添加 类型的服务器代码。【参考方案4】:

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

更新: .min 文件的固定规则

我最近花了完全没有结果的一天,试图让 C#/Net 4.6/MVC 5/Razor 中的自动捆绑(以支持自动版本控制)工作。我在 *** 和其他地方阅读了许多文章,但我找不到如何设置它的端到端演练。我也不关心文件的版本化方式(通过将带有版本的查询字符串附加到静态文件请求 - 即 somefile.js?v=1234),因为有人告诉我某些代理服务器忽略查询缓存静态资源时的字符串。

因此,经过短暂的兔子洞之旅后,我推出了自己的版本以进行自动版本控制,并在下面提供了有关如何使其工作的完整说明。

完整讨论@:Simplified Auto-Versioning of Javascript / CSS in ASP.NET MVC 5 to stop caching issues (works in Azure and Locally) With or Without URL Rewrite

问题:您的项目中通常有 2 种类型的 javascript/css 文件。

1) 很少更改的 3 方库(例如 jquery 或 mustache)(当它们更改时,文件上的版本通常会更改) - 这些可以使用 WebGrease 在“按需”的基础上捆绑/缩小或 JSCompress.com(只需在 _Layout.cshtml 中包含捆绑的文件/版本)

2) 页面特定的 css/js 文件,每当推送新构建时都应刷新。 (无需用户清除缓存或多次刷新)

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

怎么做: 每次构建项目时自动增加程序集版本,并将该数字用于特定资源上的路由静态文件喜欢保持清爽。 (所以 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 中添加以下规则。

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

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

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

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

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

对此的完整讨论:Simplified Auto-Versioning of Javascript / CSS in ASP.NET MVC 5 to stop caching issues (works in Azure and Locally) With or Without URL Rewrite(包括一种无需 URL 重写的方法)

【讨论】:

以上是关于ASP.NET MVC 中用于 CSS / JS 文件的自动版本控制?的主要内容,如果未能解决你的问题,请参考以下文章

ASP.NET MVC要怎么引入JS和CSS?

在 ASP.NET MVC 中自动版本化 CSS/JS?

在ASP.NET MVC中,使用Bundle来打包压缩js和css

本地Debug Asp.net MVC 无法加载css与js

在ASP.NET MVC中,使用Bundle来打包压缩js和css(转)

ASP.NET MVC 5 缓存 CSS 和 JS 文件