wake-up-neo.com

browser zwingen, die neuesten Js- und CSS-Dateien in der asp.net-Anwendung zu erhalten

Einige Browser zwischenspeichern JS- und CSS-Dateien und können diese nicht aktualisieren, es sei denn, Sie erzwingen dies. Was ist der einfachste Weg?.

Ich habe gerade diese Lösung implementiert, die scheinbar funktioniert. 

Deklarieren Sie eine Versionsvariable auf Ihrer Seite

  public string version { get; set; }

Rufen Sie die Versionsnummer vom web.config-Schlüssel ab

 version = ConfigurationManager.AppSettings["versionNumber"];

Rufen Sie in Ihrer aspx-Seite die Aufrufe von Javascript und Stylesheets auf

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

Wenn Sie also in Ihrer web.config die Version = 1.1 von 1.0 einstellen, lädt Ihr Browser die neuesten Dateien herunter, was Ihnen und Ihren Benutzern hoffentlich einige Frustration erspart.

Gibt es eine andere Lösung, die besser funktioniert, oder führt dies zu unvorhergesehenen Problemen für eine Website?

92
kiev

Ich habe dieses Problem gelöst, indem ich einen letzten geänderten Zeitstempel als Abfrageparameter für die Skripts anheftete. 

Ich habe dies mit einer Erweiterungsmethode gemacht und in meinen CSHTML-Dateien verwendet. Hinweis: Bei dieser Implementierung wird der Zeitstempel für 1 Minute zwischengespeichert, damit die Festplatte nicht so leise gerät.

Hier ist die Erweiterungsmethode:

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;
        }
    }
}

Und dann auf der CSHTML-Seite:

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

Im gerenderten HTML wird dies wie folgt angezeigt:

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

Ihre Lösung funktioniert. Es ist tatsächlich sehr beliebt. 

Sogar Stack Overflow verwendet eine ähnliche Methode:

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

Dabei ist v=6184 wahrscheinlich die SVN-Revisionsnummer.

26
Daniel Vassallo

ASP.NET MVC wird dies für Sie erledigen, wenn Sie Bundles für Ihre JS/CSS verwenden. Es fügt automatisch eine Versionsnummer in Form einer GUID an Ihre Bundles an und aktualisiert diese GUID nur, wenn das Bundle aktualisiert wird.

Dies ist auch hilfreich, wenn Sie eine Menge JS/CSS-Dateien haben, da dies die Ladezeiten von Inhalten erheblich verbessern kann.

Siehe hier

18
jonesy827

In ASP.NET Core (MVC 6) funktioniert dies sofort über den Tag-Helfer asp-append-version:

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

In asp.net gibt es dafür eine integrierte Möglichkeit: Bündeln . Benutze es einfach. Jede neue Version hat ein eindeutiges Suffix "? V = XXXXXXX". Im Debug-Modus ist die Bündelung deaktiviert, um die Einstellung in web.config zu aktivieren:

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

Oder fügen Sie der Methode RegisterBundles (BundleCollection-Bundles) hinzu:

BundleTable.EnableOptimizations = true;

Zum Beispiel:

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")
10
Alex Tkachuk

Ich wollte einen einfachen Liner, um den Pfad einzigartig zu machen, um den Cache zu sprengen. Das hat für mich funktioniert:

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

Wenn die Datei seit dem letzten Laden auf der Seite geändert wurde, ruft der Browser die aktualisierte Datei ab.

Er generiert den last modified-Stempel aus der .js-Datei und fügt ihn dort anstelle der Version ein, auf die möglicherweise nicht einfach zuzugreifen ist.

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

Eine andere Option könnte darin bestehen, die Prüfsumme der Datei abzurufen.

4
sniperd

Basierend auf Adam Tegans Antwort , modifiziert für die Verwendung in einer Webformularanwendung.

Im .cs-Klassencode:

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);
    }
}

In der Aspx-Auszeichnung:

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

Und im gerenderten HTML erscheint es als

<script type="text/javascript" src='/js/exampleJavaScriptFile.js?v=20150402021544'></script>
4
Bryan

Interessanterweise hat diese Site Probleme mit dem Ansatz, den Sie in Verbindung mit einigen Proxy-Setups beschrieben haben, auch wenn sie ausfallsicher sein sollte. 

Überprüfen Sie diese Meta Stack Overflow Diskussion. 

In Anbetracht dessen kann es sinnvoll sein, nicht einen GET-Parameter zu verwenden, sondern den tatsächlichen Dateinamen:

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

auch wenn dies mehr Arbeit erfordert, müssen Sie die Datei tatsächlich erstellen oder eine URL-Umschreibung dafür erstellen.

4
Pekka 웃

Hier ist ein Ansatz, der mit ASP.NET 5/MVC 6/vNext funktioniert.

Schritt 1: Erstellen Sie eine Klasse, die den letzten Schreibzeitpunkt der Datei zurückgibt, ähnlich den anderen Antworten in diesem Thread. Beachten Sie, dass hierfür ASP.NET 5 (oder andere) Abhängigkeiten erforderlich sind.

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;
     }
}

Schritt 2: Registrieren Sie den zu injizierenden Dienst in startup.cs :

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

Schritt 3: In ASP.NET 5 kann der Dienst dann direkt in eine Layoutansicht wie _Layout.cshtml eingefügt werden:

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

Es gibt einige letzte Überarbeitungen, die durchgeführt werden könnten, um physische Pfade besser zu kombinieren und den Dateinamen in einem Stil zu behandeln, der eher mit der Syntax übereinstimmt. Dies ist jedoch ein Ausgangspunkt. Hoffe, es hilft Menschen, die zu ASP.NET 5 wechseln.

3
Ender2050

Hier gibt es eine einfachere Antwort als die Antwort, die der Op in der Frage gibt (der Ansatz ist der gleiche):

Definieren Sie den Schlüssel in der web.config:

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

Rufen Sie die App-Einstellungen direkt von der Aspx-Seite aus an:

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

Ausgehend von der Antwort above habe ich den Code ein wenig geändert, damit der Helfer auch mit CSS-Dateien funktioniert und jedes Mal eine Version hinzufügt, wenn Sie Änderungen an den Dateien vornehmen, und nicht nur beim Build

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;
        }
    }
}
2
Sergi Mulà

Ich habe in meiner aspnet MVC 4-Site eine etwas andere Technik angewendet:

_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;
    }
}

Dann in den aktuellen Ansichten:

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

Vereinfachte frühere Vorschläge und Bereitstellung von Code für .NET Web Forms-Entwickler.

Dies akzeptiert sowohl relative ("~ /") als auch absolute URLs im Dateipfad der Ressource.

Fügen Sie in eine statische Erweiterungsklassendatei Folgendes ein:

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];
}

Und dann rufen Sie es in Ihrer Master-Seite auf:

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

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

Das funktioniert für mich in allen Browsern. Hier habe ich PHP verwendet, um Zufallszahlen zu generieren. Sie können Ihre eigene serverseitige Sprache verwenden.` 

1
Mukesh Rai

Holen Sie sich die geänderte Zeit der Datei, wie unten gezeigt

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

Hängen Sie dies mit der Eingabe als Querzeichenfolge an

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

Nennen Sie das vom Markup aus.

MVC Extension Helper Approach

Fügen Sie eine Erweiterungsmethode hinzu

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());
      }

    }
}

Fügen Sie diesen Namespace in web.config hinzu

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

Verwenden Sie es als Ansicht

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

Sie können die DefaultTagFormat-Eigenschaft von Scripts oder Styles überschreiben.

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

Um dieses Problem in meiner ASP.Net Ajax-Anwendung zu beheben, habe ich eine Erweiterung erstellt und dann auf der Masterseite aufgerufen. 

Für weitere Details können Sie den link durchgehen.

0
Kasim Husaini

Basierend auf der obigen Antwort habe ich eine kleine Erweiterungsklasse geschrieben, um mit CSS- und JS-Dateien zu arbeiten:

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;
        }
    }
}

Anstatt etwas zu schreiben wie:

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

Sie können jetzt schreiben:

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

Das heißt Ersetzen Sie einfach Url.Content durch Url.VersionedContent.

Generierte URLs sehen ungefähr so ​​aus:

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

Wenn Sie die Erweiterungsklasse verwenden, möchten Sie möglicherweise eine Fehlerbehandlung hinzufügen, falls der Aufruf MapPath nicht funktioniert, da contentPath keine physische Datei ist.

0
Uwe Keim

Das Hauptproblem bei dieser Vorgehensweise besteht hauptsächlich darin, dass Sie bei jeder Änderung Ihrer CSS- oder JS-Dateien daran denken müssen, Ihre Versionsnummer in Ihrem Code zu aktualisieren.

Ein möglicherweise besserer Weg ist es, einen garantierten eindeutigen Parameter für jede Ihrer css- oder js-Dateien festzulegen.

<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" />

Dadurch werden die Dateien jedes Mal vom Server angefordert, was bedeutet, dass Ihre Site beim Laden der Seite nicht so leistungsfähig ist, da diese Dateien nie zwischengespeichert werden und nicht jedes Mal unnötige Bandbreite beanspruchen.

Wenn Sie sich daran erinnern können, die Versionsnummer bei jeder Änderung zu aktualisieren, können Sie sich darüber im Klaren sein, wie Sie dies tun.

0

Ich verwende auf ähnliche Weise das Gleiche wie Sie, ohne jede Seite zu ändern. Ein PreRender-Ereignis wurde der Master-Datei hinzugefügt. Es hält meine Logik an einem Ort und gilt für Js- und CSS-Dateien.

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"]));
                    }
                }

            }

        }
    }
0
Jay