强制浏览器在asp.net应用程序中获取最新的js和css文件

时间:2010-02-02 16:49:20

标签: javascript c# asp.net .net asp.net-mvc

有些浏览器会缓存js和css文件,除非你强制它们,否则无法刷新它们。什么是最简单的方法。

我刚刚实施了这个似乎有用的解决方案。

在页面上声明版本变量

  public string version { get; set; }

从web.config键获取版本号

 version = ConfigurationManager.AppSettings["versionNumber"];

在你的aspx页面中调用javascript和样式表就像这样

<script src="scripts/myjavascript.js?v=<%=version %>" type="text/javascript"></script>
<link href="styles/mystyle.css?v=<%=version %>" rel="stylesheet" type="text/css" />

因此,如果您在web.config中将1.0版本设置为1.0,则浏览器将下载最新文件,这样可能会让您和您的用户感到沮丧。

是否有其他解决方案效果更好,或者是否会导致网站出现任何无法预料的问题?

23 个答案:

答案 0 :(得分:70)

我通过将最后修改的时间戳作为脚本的查询参数来解决此问题。

我用扩展方法做了这个,并在我的CSHTML文件中使用它。 注意:此实现将时间戳缓存1分钟,因此我们不会对磁盘​​进行过多的颠簸。

这是扩展方法:

public static class JavascriptExtension {
    public static MvcHtmlString IncludeVersionedJs(this HtmlHelper helper, string filename) {
        string version = GetVersion(helper, filename);
        return MvcHtmlString.Create("<script type='text/javascript' src='" + filename + version + "'></script>");
    }

    private static string GetVersion(this HtmlHelper helper, string filename)
    {
        var context = helper.ViewContext.RequestContext.HttpContext;

        if (context.Cache[filename] == null)
        {
            var physicalPath = context.Server.MapPath(filename);
            var version = $"?v={new System.IO.FileInfo(physicalPath).LastWriteTime.ToString("MMddHHmmss")}";
            context.Cache.Add(filename, version, null,
              DateTime.Now.AddMinutes(5), TimeSpan.Zero,
              CacheItemPriority.Normal, null);
            return version;
        }
        else
        {
            return context.Cache[filename] as string;
        }
    }
}

然后在CSHTML页面中:

 @Html.IncludeVersionedJs("/MyJavascriptFile.js")

在呈现的HTML中,显示为:

 <script type='text/javascript' src='/MyJavascriptFile.js?20111129120000'></script>

答案 1 :(得分:26)

您的解决方案有效。事实上这很受欢迎。

Even Stack Overflow使用类似的方法:

<link rel="stylesheet" href="http://sstatic.net/so/all.css?v=6184"> 

v=6184可能是SVN版本号。

答案 2 :(得分:19)

ASP.NET Core (MVC 6)中,这可以通过asp-append-version标记帮助程序开箱即用:

<script src="scripts/myjavascript.js" asp-append-version="true"></script>
<link href="styles/mystyle.css rel="stylesheet" asp-append-version="true" />

答案 3 :(得分:18)

如果您使用JS / CSS捆绑包,ASP.NET MVC将为您处理此问题。它会自动将GUID形式的版本号附加到您的包中,并且只在更新包时更新此GUID(即任何源文件都有更改)。

如果你有大量的JS / CSS文件,这也会有所帮助,因为它可以大大改善内容加载时间!

See Here

答案 4 :(得分:10)

asp.net中有一种内置方式:捆绑。只是使用它。每个新版本都有唯一的后缀“?v = XXXXXXX”。在调试模式下,捆绑已关闭,用于在web.config中启用make设置:

<system.web>
    <compilation debug="false" />
</system.web>

或者添加到方法RegisterBundles(BundleCollection包):

BundleTable.EnableOptimizations = true;

例如:

BundleConfig.cs:

bundles.Add(new ScriptBundle("~/Scripts/myjavascript.js")
                .Include("~/Scripts/myjavascript.js"));

bundles.Add(new StyleBundle("~/Content/mystyle.css")
                .Include("~/Content/mystyle.css"));

_Layout.cshtml:

@Scripts.Render("~/Scripts/myjavascript.js")
@Styles.Render("~/Content/mystyle.css")

答案 5 :(得分:4)

我想要一个简单的衬板,使该路径成为破坏缓存的唯一路径。这对我有用:

<script src="scripts/main.js?bust_js_cache=<%=System.IO.File.GetLastWriteTime(Server.MapPath("scripts/main.js")).ToString("HH:mm:ss")%>" type="text/javascript"></script>

如果文件自上次加载到页面以来已被修改,浏览器将拉出更新的文件。

它从last modified文件生成.js戳记,并将其卡在其中,而不是可能不容易获得访问权限的版本。

<script src="scripts/main.js?bust_js_cache=10:18:38" type="text/javascript"></script>

另一种选择是获取文件的校验和。

答案 6 :(得分:4)

基于Adam Tegan's answer,已修改,可在网络表单应用程序中使用。

在.cs类代码中:

Allow from all

在aspx标记中:

public static class FileUtility
{
    public static string SetJsVersion(HttpContext context, string filename) {
        string version = GetJsFileVersion(context, filename);
        return filename + version;
    }

    private static string GetJsFileVersion(HttpContext context, string filename)
    {
        if (context.Cache[filename] == null)
        {
            string filePhysicalPath = context.Server.MapPath(filename);

            string version = "?v=" + GetFileLastModifiedDateTime(context, filePhysicalPath, "yyyyMMddhhmmss");

            return version;
        }
        else
        {
            return string.Empty;
        }
    }

    public static string GetFileLastModifiedDateTime(HttpContext context, string filePath, string dateFormat)
    {
        return new System.IO.FileInfo(filePath).LastWriteTime.ToString(dateFormat);
    }
}

在呈现的HTML中,它显示为

<script type="text/javascript" src='<%= FileUtility.SetJsVersion(Context,"/js/exampleJavaScriptFile.js") %>'></script>

答案 7 :(得分:4)

这个答案比问题中的op给出的答案更简单(方法是相同的):

在web.config中定义密钥:

<add key="VersionNumber" value="06032014"/>

直接从aspx页面调用appsetting:

<link href="styles/navigation.css?v=<%=ConfigurationManager.AppSettings("VersionNumber")%>" rel="stylesheet" type="text/css" />

答案 8 :(得分:4)

有趣的是,这个网站在您描述的与某些代理设置相关的方法上存在问题,即使它应该是故障安全的。

查看此Meta Stack Overflow讨论。

因此,考虑到这一点,不使用GET参数进行更新可能是有意义的,但实际的文件名是:

href="/css/scriptname/versionNumber.css" 

尽管这还有很多工作要做,因为您必须实际创建文件,或者为其构建URL重写。

答案 9 :(得分:3)

这是一种适用于 ASP.NET 5 / MVC 6 / vNext 的方法。

步骤1:创建一个类以返回文件的上次写入时间,类似于此线程中的其他答案。注意,这需要ASP.NET 5(或其他)依赖注入。

public class FileVersionService
{
    private IHostingEnvironment _hostingEnvironment;
    public FileVersionService(IHostingEnvironment hostingEnvironment)
    {
        _hostingEnvironment = hostingEnvironment;
    }

    public string GetFileVersion(string filename)
    {
       var path = string.Format("{0}{1}", _hostingEnvironment.WebRootPath, filename);
       var fileInfo = new FileInfo(path);
       var version = fileInfo.LastWriteTimeUtc.ToString("yyyyMMddhhmmssfff");
       return version;
     }
}

第2步:注册要在 startup.cs 内注入的服务:

public void ConfigureServices(IServiceCollection services)
{
    ...
    services.AddScoped<FileVersionService>();
    ...
}

第3步:然后,在ASP.NET 5中,可以将服务直接注入布局视图,例如 _Layout.cshtml ,如下所示:

@inject Namespace.Here.FileVersionService fileVersionService
<!DOCTYPE html>
<html lang="en" class="@ViewBag.HtmlClass">
<head>
    ...
    <link href="/css/styles.css?v=@fileVersionService.GetFileVersion("\\css\\styles.css")" rel="stylesheet" />
    ...
</head>
<body>
    ...
</body>

可以采取一些最后的措施来更好地组合物理路径并以更符合语法的方式处理文件名,但这是一个起点。希望它能帮助人们转向ASP.NET 5.

答案 10 :(得分:2)

above answer开始,我修改了一些代码,使得帮助程序也可以使用CSS文件,并且每次在文件中进行一些更改时添加一个版本,而不仅仅是在进行构建时< / p>

public static class HtmlHelperExtensions
{
    public static MvcHtmlString IncludeVersionedJs(this HtmlHelper helper, string filename)
    {
        string version = GetVersion(helper, filename);
        return MvcHtmlString.Create("<script type='text/javascript' src='" + filename + version + "'></script>");
    }

    public static MvcHtmlString IncludeVersionedCss(this HtmlHelper helper, string filename)
    {
        string version = GetVersion(helper, filename);
        return MvcHtmlString.Create("<link href='" + filename + version + "' type ='text/css' rel='stylesheet'/>");
    }

    private static string GetVersion(this HtmlHelper helper, string filename)
    {
        var context = helper.ViewContext.RequestContext.HttpContext;
        var physicalPath = context.Server.MapPath(filename);
        var version = "?v=" +
        new System.IO.FileInfo(physicalPath).LastWriteTime
        .ToString("yyyyMMddHHmmss");
        context.Cache.Add(physicalPath, version, null,
          DateTime.Now.AddMinutes(1), TimeSpan.Zero,
          CacheItemPriority.Normal, null);

        if (context.Cache[filename] == null)
        {
            context.Cache[filename] = version;
            return version;
        }
        else
        {
            if (version != context.Cache[filename].ToString())
            {
                context.Cache[filename] = version;
                return version;
            }
            return context.Cache[filename] as string;
        }
    }
}

答案 11 :(得分:2)

我在aspnet MVC 4网站中使用了一种略有不同的技术:

_ViewStart.cshtml:

@using System.Web.Caching
@using System.Web.Hosting
@{
    Layout = "~/Views/Shared/_Layout.cshtml";
    PageData.Add("scriptFormat", string.Format("<script src=\"{{0}}?_={0}\"></script>", GetDeployTicks()));
}

@functions
{

    private static string GetDeployTicks()
    {
        const string cacheKey = "DeployTicks";
        var returnValue = HttpRuntime.Cache[cacheKey] as string;
        if (null == returnValue)
        {
            var absolute = HostingEnvironment.MapPath("~/Web.config");
            returnValue = File.GetLastWriteTime(absolute).Ticks.ToString();
            HttpRuntime.Cache.Insert(cacheKey, returnValue, new CacheDependency(absolute));
        }
        return returnValue;
    }
}

然后在实际观点中:

 @Scripts.RenderFormat(PageData["scriptFormat"], "~/Scripts/Search/javascriptFile.min.js")

答案 12 :(得分:2)

<?php $rand_no = rand(10000000, 99999999)?> <script src="scripts/myjavascript.js?v=<?=$rand_no"></script>

这适用于所有浏览器。在这里,我使用PHP生成随机数。您可以使用自己的服务器端语言。

答案 13 :(得分:1)

获取文件修改时间,如下所示

private static string GetLastWriteTimeForFile(string pathVal)
    {
        return System.IO.File.GetLastWriteTime(HostingEnvironment.MapPath(pathVal)).ToFileTime().ToString();
    }

使用输入作为查询字符串

添加此项
public static string AppendDateInFile(string pathVal)
    {
        var patheWithDate = new StringBuilder(pathVal);
        patheWithDate.AppendFormat("{0}x={1}",
                               pathVal.IndexOf('?') >= 0 ? '&' : '?',
                               GetLastWriteTimeForFile(pathVal));
        return patheWithDate.ToString();
    }

从标记中调用此方法。

MVC扩展助手方法

添加扩展方法

namespace TNS.Portal.Helpers
{
    public static class ScriptExtensions
    {
        public static HtmlString QueryStringScript<T>(this HtmlHelper<T> html, string path)
        {
            var file = html.ViewContext.HttpContext.Server.MapPath(path);
            DateTime lastModified = File.GetLastWriteTime(file);
            TagBuilder builder = new TagBuilder("script");
            builder.Attributes["src"] = path + "?modified=" + lastModified.ToString("yyyyMMddhhmmss");
            return new HtmlString(builder.ToString());
        }

       public static HtmlString QueryStringStylesheet<T>(this HtmlHelper<T> html, string path)
       {
        var file = html.ViewContext.HttpContext.Server.MapPath(path);
        DateTime lastModified = File.GetLastWriteTime(file);
        TagBuilder builder = new TagBuilder("link");
        builder.Attributes["href"] = path + "?modified=" + lastModified.ToString("yyyyMMddhhmmss");
        builder.Attributes["rel"] = "stylesheet";
        return new HtmlString(builder.ToString());
      }

    }
}

在web.config中添加此命名空间

<system.web.webPages.razor>
    <host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
    <pages pageBaseType="System.Web.Mvc.WebViewPage">
      <namespaces>
        <add namespace="System.Web.Mvc" />
        <add namespace="System.Web.Mvc.Ajax" />
        <add namespace="System.Web.Mvc.Html" />
        <add namespace="System.Web.Optimization"/>
        <add namespace="System.Web.Routing" />
        <add namespace="TNS.Portal" />
        <add namespace="TNS.Portal.Helpers" />
      </namespaces>
    </pages>
  </system.web.webPages.razor>

在视图中将其用作

@Html.QueryStringScript("/Scripts/NPIAjaxCalls.js")
@Html.QueryStringStylesheet("/Content/StyledRadio.css")

答案 14 :(得分:1)

简化先前的建议并为.NET Web Forms开发人员提供代码。

这将接受资源文件路径中的相对(“〜/”)和绝对URL。

放入一个静态扩展类文件,如下:

public static string VersionedContent(this HttpContext httpContext, string virtualFilePath)
{
    var physicalFilePath = httpContext.Server.MapPath(virtualFilePath);
    if (httpContext.Cache[physicalFilePath] == null)
    {
        httpContext.Cache[physicalFilePath] = ((Page)httpContext.CurrentHandler).ResolveUrl(virtualFilePath) + (virtualFilePath.Contains("?") ? "&" : "?") + "v=" + File.GetLastWriteTime(physicalFilePath).ToString("yyyyMMddHHmmss");
    }
    return (string)httpContext.Cache[physicalFilePath];
}

然后在您的母版页中调用它:

<link type="text/css" rel="stylesheet" href="<%= Context.VersionedContent("~/styles/mystyle.css") %>" />
<script type="text/javascript" src="<%= Context.VersionedContent("~/scripts/myjavascript.js") %>"></script>

答案 15 :(得分:0)

这样做的主要问题主要是每次对css或js文件进行任何更改时,都需要记住更新代码中的版本号。

可能更好的方法是为每个css或js文件设置一个保证唯一参数,如下所示:

<script src="scripts/myjavascript.js?_=<%=DateTime.Now.Ticks%>" type="text/javascript"></script>
<link href="styles/mystyle.css?_=<%=DateTime.Now.Ticks%>" rel="stylesheet" type="text/css" />

这会强制每次都从服务器请求文件,这也意味着您的网站在页面加载时不会表现得那么高,因为这些文件永远不会被缓存,并且每次都会使用不需要的带宽。

基本上,如果您记得在每次更改时都更新版本号,那么您可以放弃自己的工作方式。

答案 16 :(得分:0)

通过以下概念在.net应用程序中轻松便捷地实现CSS版本控制。.无需编写后端代码。

<link href="<%="../../App_Themes/Base/css/main.css?v="+ DateTime.Now.ToString("yyyyMMddhhmmss") +""%>" rel="stylesheet" />

答案 17 :(得分:0)

为了解决我的ASP.Net Ajax应用程序中的此问题,我创建了一个扩展,然后在母版页中调用。

有关更多详细信息,您可以浏览link

答案 18 :(得分:0)

您可以覆盖Scripts或Styles的DefaultTagFormat属性。

Scripts.DefaultTagFormat = @"<script src=""{0}?v=" + ConfigurationManager.AppSettings["pubversion"] + @"""></script>";
Styles.DefaultTagFormat = @"<link href=""{0}?v=" + ConfigurationManager.AppSettings["pubversion"] + @""" rel=""stylesheet""/>";

答案 19 :(得分:0)

我使用类似的方法在不修改每个页面的情况下执行相同的操作。添加了PreRender事件是主文件。它将我的逻辑保存在一个地方并适用于js和css文件。

protected void Page_PreRender(object sender, EventArgs e)
    {
        HtmlLink link = null;
        LiteralControl script = null;


        foreach (Control c in Header.Controls)
        {
            //StyleSheet add version
            if (c is HtmlLink)
            {
                link = c as HtmlLink;


                if (link.Href.EndsWith(".css", StringComparison.InvariantCultureIgnoreCase))
                {
                    link.Href += string.Format("?v={0}", ConfigurationManager.AppSettings["agVersion"]);
                }

            }

            //Js add version
            if (c is LiteralControl)
            {
                script = c as LiteralControl;

                if (script.Text.Contains(".js"))
                {
                    var foundIndexes = new List<int>();


                    for (int i = script.Text.IndexOf(".js\""); i > -1; i = script.Text.IndexOf(".js\"", i + 1))
                    {

                        foundIndexes.Add(i);
                    }

                    for (int i = foundIndexes.Count - 1; i >= 0; i--)
                    {

                        script.Text = script.Text.Insert(foundIndexes[i] + 3, string.Format("?v={0}", ConfigurationManager.AppSettings["agVersion"]));
                    }
                }

            }

        }
    }

答案 20 :(得分:0)

基于the above answer我已经编写了一个小扩展类来处理CSS和JS文件:

public static class TimestampedContentExtensions
{
    public static string VersionedContent(this UrlHelper helper, string contentPath)
    {
        var context = helper.RequestContext.HttpContext;

        if (context.Cache[contentPath] == null)
        {
            var physicalPath = context.Server.MapPath(contentPath);
            var version = @"v=" + new FileInfo(physicalPath).LastWriteTime.ToString(@"yyyyMMddHHmmss");

            var translatedContentPath = helper.Content(contentPath);

            var versionedContentPath =
                contentPath.Contains(@"?")
                    ? translatedContentPath + @"&" + version
                    : translatedContentPath + @"?" + version;

            context.Cache.Add(physicalPath, version, null, DateTime.Now.AddMinutes(1), TimeSpan.Zero,
                CacheItemPriority.Normal, null);

            context.Cache[contentPath] = versionedContentPath;
            return versionedContentPath;
        }
        else
        {
            return context.Cache[contentPath] as string;
        }
    }
}

而不是写下像:

<link href="@Url.Content(@"~/Content/bootstrap.min.css")" rel="stylesheet" type="text/css" />
<script src="@Url.Content(@"~/Scripts/bootstrap.min.js")"></script>

您现在可以写:

<link href="@Url.VersionedContent(@"~/Content/bootstrap.min.css")" rel="stylesheet" type="text/css" />
<script src="@Url.VersionedContent(@"~/Scripts/bootstrap.min.js")"></script>

即。只需将Url.Content替换为Url.VersionedContent

生成的网址类似于:

<link href="/Content/bootstrap.min.css?v=20151104105858" rel="stylesheet" type="text/css" />
<script src="/Scripts/bootstrap.min.js?v=20151029213517"></script>

如果您使用扩展程序类,则可能需要添加错误处理,以防MapPath调用无效,因为contentPath不是物理文件。

答案 21 :(得分:-1)

对于ASP.NET页面,我使用以下

<强> BEFORE

<script src="/Scripts/pages/common.js" type="text/javascript"></script>

AFTER(强制重新加载)

 <script src="/Scripts/pages/common.js?ver<%=DateTime.Now.Ticks.ToString()%>" type="text/javascript"></script>

添加DateTime.Now.Ticks可以很好地工作。

答案 22 :(得分:-3)

只需将其放在 web.config 中的 system.webserver 中

<caching enabled="true" enableKernelCache="true">
      <profiles>
        <add extension=".html" policy="CacheUntilChange" kernelCachePolicy="CacheUntilChange"/>
        <add extension=".css" policy="CacheUntilChange" kernelCachePolicy="CacheUntilChange"/>
        <add extension=".js" policy="CacheUntilChange" kernelCachePolicy="CacheUntilChange"/>
      </profiles>
    </caching>