wymusza pobieranie przez przeglądarki najnowszych plików js i css w aplikacji asp.net

104

Niektóre przeglądarki buforują pliki js i css, nie mogą ich odświeżyć, chyba że zostanie to narzucone. Jaki jest najłatwiejszy sposób.

Właśnie wdrożyłem to rozwiązanie, które wydaje się działać.

Zadeklaruj zmienną wersji na swojej stronie

  public string version { get; set; }

Uzyskaj numer wersji z klucza web.config

 version = ConfigurationManager.AppSettings["versionNumber"];

Na stronie aspx wykonaj wywołania javascript i arkuszy stylów w ten sposób

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

Jeśli więc ustawisz wersję 1.1 z 1.0 w swoim web.config, Twoja przeglądarka pobierze najnowsze pliki, co, miejmy nadzieję, zaoszczędzi Tobie i Twoim użytkownikom pewnej frustracji.

Czy istnieje inne rozwiązanie, które działa lepiej, czy też spowoduje to nieprzewidziane problemy na stronie internetowej?

Kijów
źródło
Ciekawe pytanie, ostatnio miałem ten sam problem, ale był to problem tylko podczas testów deweloperskich. Nie przejmowałem się tym zbytnio, ponieważ nie zamierzamy zmieniać tych plików po uruchomieniu. Chciałbym jednak poznać rozwiązanie do wykorzystania w przyszłości!
Brett Allen
Jedynym problemem, jaki widzę, jest to, że zmiany w pliku web.config będą w tle wywoływać ponowne uruchomienie aplikacji: msdn.microsoft.com/en-us/library/aa478432.aspx
monty
Dziękuję za pytanie. To pomogło mi w rozwiązaniu dużego problemu.
Reddy

Odpowiedzi:

76

Rozwiązałem to, dołączając ostatnio zmodyfikowany znacznik czasu jako parametr zapytania do skryptów.

Zrobiłem to za pomocą metody rozszerzenia i używając jej w moich plikach CSHTML. Uwaga: ta implementacja buforuje znacznik czasu przez 1 minutę, więc nie wyrzucamy zbyt mocno dysku.

Oto metoda rozszerzenia:

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

A potem na stronie CSHTML:

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

W wyrenderowanym kodzie HTML wygląda to tak:

 <script type='text/javascript' src='/MyJavascriptFile.js?20111129120000'></script>
Adam Tegen
źródło
1
to jest świetne dla mvc, zastanawiam się, czy najnowszy framework mvc 5 poradzi sobie z tym problemem? Podejście stosowane w sprzedaży wiązanej z symbolem wieloznacznym - {wersja} jest również sposobem rozwiązania tego problemu, jednak wymaga zmiany nazw plików po każdej nowej kompilacji ...
kijów
Używam podstaw Twojego przykładu MVC w witrynie internetowej z formularzami internetowymi i działa świetnie, więc dziękuję za udostępnienie!
Bryan
Czy powinien być jakiś powód do obaw związanych z wyświetlaniem daty / czasu ostatniej modyfikacji dla plików zasobów? Jeśli używany plik miał datę / godzinę sprzed kilku lat, czy może istnieć informacja, że ​​firma nie chciałaby być publiczna dla swoich użytkowników?
Bryan
To standardowy sposób, następuje większość aplikacji. Ale ten znacznik czasu powinien się zmienić tylko podczas wdrażania lub tworzenia aplikacji. W przeciwnym razie za każdym razem, gdy użytkownik odświeży stronę lub przełączy się na inne strony w aplikacji. Przeglądarka ponownie pobierze wszystkie arkusze stylów i javascript, co nie jest dobre
Tarun
2
Jaki jest wpływ na wydajność strony? Jakie opóźnienie może spowodować załadowanie strony?
Durgesh Sonawane
28

Twoje rozwiązanie działa. W rzeczywistości jest dość popularny.

Even Stack Overflow używa podobnej metody:

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

Gdzie v=6184jest prawdopodobnie numer wersji SVN.

Daniel Vassallo
źródło
Byłoby to znacznie bardziej podatne podejście niż to opisane w przyjętej odpowiedzi. Sprawdzanie wersji SVN pliku za każdym razem, gdy wyświetlana jest strona, jest obciążeniem wynikającym z wydajności, zwłaszcza że liczba użytkowników rośnie w czasie.
Neolisk
4
Możesz uzyskać numer wersji podczas kompilacji, zapisać go do pliku (np. Częściowego pliku .cs), dołączyć ten plik do swojego projektu, więc nie musisz czytać go ze svn w czasie wykonywania. Użyłem tego podejścia z msbuild do umieszczenia numerów wersji w moich plikach AssemblyInfo.cs z svn.
Ramazan Binarbasi
2
Korzystanie z globalnego numeru wersji / rewizji ma co najmniej jedną wadę: opublikowanie aktualizacji witryny unieważnia pamięci podręczne przeglądarki dla wszystkich plików .js i .css, a nie tylko tych, które uległy zmianie. Prawdopodobnie nie ma to znaczenia w większości zastosowań, ale wspominam o tym dla kompletności.
Adam Tegen
Jeśli automatycznie zaktualizujesz wersję plików assemblyinfo.cs podczas wdrażania lub kompilacji, dla tego numeru można użyć wersji pomocniczej.
kristianp
28

W ASP.NET Core (MVC 6) działa to po wyjęciu z pudełka za pośrednictwem asp-append-versionpomocnika tagów: In ASP.NET Core (MVC 6) this works out of the box via the tag helper:

<script src="scripts/myjavascript.js" asp-append-version="true"></script>
<link href="styles/mystyle.css rel="stylesheet" asp-append-version="true" />
metalheart
źródło
1
Dziękujemy za poinformowanie nas! Nie wiedziałem tego wcześniej!
Federico Navarrete
18

ASP.NET MVC poradzi sobie z tym za Ciebie, jeśli używasz pakietów dla swojego JS / CSS. Automatycznie doda numer wersji w postaci identyfikatora GUID do twoich pakietów i zaktualizuje ten identyfikator GUID tylko wtedy, gdy pakiet zostanie zaktualizowany (inaczej każdy z plików źródłowych ma zmiany).

Pomaga to również, jeśli masz mnóstwo plików JS / CSS, ponieważ może znacznie poprawić czas ładowania treści!

Spójrz tutaj

jonesy827
źródło
Czy masz na myśli, że jeśli używamy pakietów w aplikacji MVC, nie ma potrzeby żadnej z metod w zamieszczonej tutaj odpowiedzi? Jeśli tak, sprzedaż wiązana jest o wiele ważniejsza, niż kiedykolwiek myślałem. Czy mógłbyś wyjaśnić nam tę kwestię? Dzięki.
Jack
1
Tak, dokładnie. Jeśli twoje skrypty są zawarte w pakiecie, automatycznie generuje numer wersji dla każdego pakietu po wykryciu zmian w którymkolwiek z plików źródłowych pakietu.
jonesy827
I nie zapominaj, że jako programista nadal masz bóle głowy. Pakowanie ASP.NET w żaden sposób nie pomaga podczas debugowania i programowania.
it3xl
12

W asp.net istnieje wbudowany sposób: sprzedaż wiązana . Po prostu go użyj. Każda nowa wersja będzie miała unikalny sufiks „? V = XXXXXXX”. W trybie debugowania pakietowanie jest wyłączone, aby włączyć ustawienia w web.config:

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

Lub dodaj do metody RegisterBundles (pakiety BundleCollection):

BundleTable.EnableOptimizations = true;

Na przykład:

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")
Alex Tkachuk
źródło
Ale to zadziała tylko w środowiskach wydawniczych lub produkcyjnych. A co z programowaniem, gdy włączony jest tryb debugowania? Czy pakiet nadal rozwiązuje ten problem?
VAAA
Tak, bundlind nie ułatwia programistom życia. Musisz nacisnąć Ctrl-F5 po każdej zmianie skryptu. A jeśli masz ramki, będzie jeszcze zabawniej.
it3xl
7

Jest na to prostsza odpowiedź niż odpowiedź udzielona przez op w pytaniu (podejście jest takie samo):

Zdefiniuj klucz w pliku web.config:

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

Wykonaj wywołanie appSettings bezpośrednio ze strony aspx:

<link href="styles/navigation.css?v=<%=ConfigurationManager.AppSettings["VersionNumber"]%>" rel="stylesheet" type="text/css" />
JackArbiter
źródło
Podoba mi się to, ale obawiam się, dlaczego to rozwiązanie ma tak mało głosów ...
SimplyInk
@SimplyInk Nie wiem, ale jest 20 różnych odpowiedzi, więc może to mieć z tym coś wspólnego. Jeśli to działa i Ci się podoba, nie krępuj się zagłosować.
JackArbiter
4

Na podstawie odpowiedzi Adama Tegana , zmodyfikowanej do użytku w aplikacji formularzy internetowych.

W kodzie klasy .cs:

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

W znaczniku aspx:

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

A w wyrenderowanym kodzie HTML wygląda jak

<script type="text/javascript" src='/js/exampleJavaScriptFile.js?v=20150402021544'></script>
Bryan
źródło
2
Hej! Twój przykład działa, ale powinieneś albo usunąć odwołania do pamięci podręcznej, albo naprawić kod, aby korzystał z pamięci podręcznej, ponieważ może być niejasne, dlaczego jej używasz. Aby skorzystać z pamięci podręcznej, należy dodać wersję pliku do pamięci podręcznej za pomocą metody context.Cache.Add w przypadku context.Cache [nazwa_pliku] == null. Jeśli context.Cache [nazwa_pliku]! = Null, powinieneś zwrócić wartość z pamięci podręcznej (context.Cache [nazwa pliku])
Flavia Obreja
1
Flavia, myślę, że twoje wyjaśnienie ma sens i myślę, że jest to prostsza, wydajniejsza implementacja. Dziękujemy za przesłanie pomocnych komentarzy i opinii.
Bryan,
4

Co ciekawe, ta sama witryna ma problemy z podejściem, które opisujesz w związku z niektórymi konfiguracjami proxy, mimo że powinno być bezpieczne.

Sprawdź tę dyskusję dotyczącą przepełnienia Meta Stack .

W związku z tym rozsądne może być nie używanie parametru GET do aktualizacji, ale rzeczywista nazwa pliku:

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

nawet jeśli jest to więcej pracy do wykonania, ponieważ będziesz musiał faktycznie utworzyć plik lub napisać dla niego przepisany adres URL.

Pekka 웃
źródło
4

Chciałem prostego, jednego linera, aby ścieżka była unikalna, aby zniszczyć pamięć podręczną. To zadziałało dla mnie:

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

Jeśli plik został zmodyfikowany od czasu ostatniego załadowania go na stronę, przeglądarka pobierze zaktualizowany plik.

Generuje last modifiedstempel z .jspliku i wsuwa go tam zamiast wersji, do której dostęp może nie być łatwy.

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

Inną opcją może być pobranie sumy kontrolnej pliku.

sniperd
źródło
1
Zdecydowanie najłatwiejszy sposób na zrobienie tego i prawdopodobnie najniższe koszty ogólne.
Tony Hinkle,
1
Idealne rozwiązanie. Przetestowałem również Chrome 70, Firefox 63 i IE 11, aby upewnić się, że buforowanie rzeczywiście działa. To jest. To tylko niszczy buforowanie w nowych wersjach pliku, przynajmniej w najnowszych wersjach przeglądarek. Słyszałem gdzie indziej wzmiankę, że niektóre przeglądarki przeładowują każdy plik z querystringiem (?). Może tak było, a może nadal tak jest w przypadku Safari i Opery. DK.
Brad Mathews
3

Oto podejście, które działa z ASP.NET 5 / MVC 6 / vNext .

Krok 1: Utwórz klasę, która będzie zwracać czas ostatniego zapisu pliku, podobnie jak w przypadku innych odpowiedzi w tym wątku. Uwaga: wymaga to iniekcji zależności ASP.NET 5 (lub innej).

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

Krok 2: Zarejestruj usługę, która ma zostać wstrzyknięta w startup.cs :

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

Krok 3: Następnie w ASP.NET 5 można wstrzyknąć usługę bezpośrednio do widoku układu, takiego jak _Layout.cshtml w następujący sposób:

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

Istnieje kilka ostatnich poprawek, które można zrobić, aby lepiej połączyć ścieżki fizyczne i obsłużyć nazwę pliku w stylu bardziej zgodnym ze składnią, ale to jest punkt wyjścia. Mam nadzieję, że pomoże to ludziom przejść na ASP.NET 5.

Ender2050
źródło
to zachowanie jest faktycznie obsługiwane po wyjęciu z pudełka, zobacz moją odpowiedź
metalheart
3

W mojej witrynie ASPNET MVC 4 zastosowałem nieco inną technikę:

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

Następnie w rzeczywistych widokach:

 @Scripts.RenderFormat(PageData["scriptFormat"], "~/Scripts/Search/javascriptFile.min.js")
Anthony Wolfe
źródło
3

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

Działa to dla mnie we wszystkich przeglądarkach. Tutaj użyłem PHP do wygenerowania losowego nie. Możesz użyć własnego języka po stronie serwera .`

Mukesh Rai
źródło
Dobra odpowiedź, ale ASP MVC może być nieco problematyczny, jeśli nie weźmiesz pod uwagę tego, co wyjaśnił Adam, ponieważ próbowałem, a folder Bundle nie rozpoznaje go, jeśli pracujesz z MVC 5. Ale dzięki za sugestię!
Federico Navarrete
2

Zaczynając od powyższej odpowiedzi zmodyfikowałem trochę kod, aby pomocnik działał również z plikami CSS i dodawałem wersję za każdym razem, gdy robisz jakąś zmianę w plikach, a nie tylko podczas budowania

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;
        }
    }
}
Sergi Mulà
źródło
1

Uzyskaj czas modyfikacji pliku, jak pokazano poniżej

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

Dołącz to z wejściem jako querystring

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

Nazwij to ze znaczników.

Podejście pomocnika rozszerzenia MVC

Dodaj metodę rozszerzenia

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

    }
}

Dodaj tę przestrzeń nazw w 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>

Użyj go w widoku jako

@Html.QueryStringScript("/Scripts/NPIAjaxCalls.js")
@Html.QueryStringStylesheet("/Content/StyledRadio.css")
Lijo
źródło
1

Uproszczone wcześniejsze sugestie i udostępnienie kodu dla programistów .NET Web Forms.

Spowoduje to zaakceptowanie względnych („~ /”) i bezwzględnych adresów URL w ścieżce pliku do zasobu.

Umieść w statycznym pliku klasy rozszerzeń, następujący:

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

A potem nazwij to na swojej stronie wzorcowej tak:

<link type="text/css" rel="stylesheet" href="<%= Context.VersionedContent("~/styles/mystyle.css") %>" />
<script type="text/javascript" src="<%= Context.VersionedContent("~/scripts/myjavascript.js") %>"></script>
Jason Ellingson
źródło
Miłe podejście też!
Federico Navarrete
0

Na podstawie powyższej odpowiedzi napisałem małą klasę rozszerzenia do pracy z plikami CSS i 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;
        }
    }
}

Zamiast pisać coś takiego:

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

Możesz teraz napisać:

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

Czyli po prostu wymienić Url.Contentsię Url.VersionedContent.

Wygenerowane adresy URL wyglądają mniej więcej tak:

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

Jeśli używasz klasy rozszerzenia, możesz dodać obsługę błędów w przypadku, gdy MapPathpołączenie nie działa, ponieważ contentPathnie jest to plik fizyczny.

Uwe Keim
źródło
0

Używam podobnego sposobu, aby zrobić to samo, co robisz, bez modyfikowania każdej strony. Dodano zdarzenie PreRender to plik główny. Utrzymuje moją logikę w jednym miejscu i ma zastosowanie zarówno do plików js, jak i 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"]));
                    }
                }

            }

        }
    }
Sójka
źródło
0

Możesz przesłonić właściwość DefaultTagFormat skryptów lub stylów.

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

Aby rozwiązać ten problem w mojej aplikacji ASP.Net Ajax, utworzyłem rozszerzenie, a następnie wywołałem na stronie wzorcowej.

Aby uzyskać więcej informacji, możesz przejść przez link .

Kasim Husaini
źródło
0

Łatwy i sprytny sposób na implementację wersji css w aplikacji .net według poniższej koncepcji. Nie ma potrzeby pisania kodu zaplecza.

<link href="<%="../../App_Themes/Base/css/main.css?v="+ DateTime.Now.ToString("yyyyMMddhhmmss") +""%>" rel="stylesheet" />
SantoshK
źródło
wymusi to pobieranie przy każdym renderowaniu strony, nawet jeśli pliki w ogóle się nie zmieniły.
Thanasis Ioannidis
@ThanasisIoannidis Może używać tam, gdzie pliki są regularnie zmieniane. Inną opcją jest dodanie klucza appVersion w pliku web.config i używanie go z nazwą pliku .. ale musisz zaktualizować, kiedy wypuszczasz aplikację do wersji prod.
SantoshK
-1

Główny problem z robieniem tego w ten sposób polega głównie na tym, że będziesz musiał pamiętać o aktualizowaniu numeru wersji w kodzie za każdym razem, gdy wprowadzasz jakiekolwiek zmiany w plikach css lub js.

Prawdopodobnie lepszym sposobem na to jest ustawienie gwarantowanego unikalnego parametru dla każdego pliku css lub js, na przykład:

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

Wymusza to za każdym razem żądanie plików z serwera, co oznacza również, że Twoja witryna nie będzie tak wydajna po załadowaniu strony, ponieważ te pliki nigdy nie będą buforowane i za każdym razem będą wykorzystywać niepotrzebną przepustowość.

Zasadniczo, jeśli pamiętasz o aktualizowaniu numeru wersji za każdym razem, gdy wprowadzana jest zmiana, możesz uciec od tego, jak to robisz.

Tim S. Van Haren
źródło
9
i wykorzystuje również duże ilości przepustowości.
Darren Kopp
2
Racja, nie chcesz nowej wersji JS przy każdym ładowaniu strony ... po prostu chcesz, aby przeglądarka szukała nowej wersji za każdym razem, gdy faktycznie masz zaktualizowaną wersję.
kingdango
Jest to całkowicie akceptowalne w przypadku rozwiązania tymczasowego w pliku CSS o rozmiarze 50 KB podczas opracowywania. +1
Colbs
-2

W przypadku stron ASP.NET używam następujących plików

PRZED

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

PO (wymuszenie przeładowania)

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

Dodanie DateTime.Now.Ticks działa bardzo dobrze.

Ravi Ram
źródło
tak, problem dotyczy przepustowości - jak w komentarzach powyżej stackoverflow.com/a/2185918/59508
kijów