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

时间:2011-01-30 06:07:33

标签: asp.net-mvc url-rewriting url-routing

我已经阅读了很多关于如何自动编辑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>

THX

4 个答案:

答案 0 :(得分:15)

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

编辑:

根据以下评论中的讨论,我更新了此代码:

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}/v{2}/assets/img/{1}", GetAssetsRoot(), fileName, _version));
    }

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

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

    public static string Script(this UrlHelper helper, string fileName)
    {
        return helper.Content(string.Format("{0}/v{2}/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上创建重写规则。

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

答案 1 :(得分:0)

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

<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=,但在某处的配置文件中将版本参数附加到资源上并不是非常困难。

修改

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

答案 2 :(得分:0)

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

以这种方式包含的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();
    }
    ...
}

答案 3 :(得分:0)

更新:以前的版本无法在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中自动捆绑(支持自动版本控制)。我在StackOverflow和其他地方阅读了很多文章,但我找不到如何设置它的端到端的演练。我也不关心文件的版本编辑方式(通过将带有版本的查询字符串附加到静态文件请求 - 即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

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

1)3个派对库(例如jquery或小胡子)很少改变(当它们发生时,文件上的版本通常会发生变化) - 这些可以根据需要捆绑/缩小&#34;根据需要&# 34;使用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文件中(位于项目的&#34;属性&#34;部分下面)更改以下行:

[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中为包含嵌入版本slugs的文件设置url重写(参见步骤3)

在web.config(项目的主要部分)中,在system.webServer中添加以下规则。

<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文件中创建版本slu。

在Global.asax.cs中找到

(在项目的根目录中找到)将以下代码添加到protected 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中,在我的头部分中,我有以下样式表代码块:

<!-- 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

中的代码处理

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

<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手动创建的所有第三方库的捆绑包。如果我添加或更改捆绑包中的任何文件(这是罕见的),那么我手动将文件重命名为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 (包括在没有网址重写的情况下执行此操作)