Автоматическое управление версиями в ASP.NET MVC для файлов CSS / JS?

Я прочитал много статей о том, как автоматически создавать версии ваших файлов CSS/JS, но ни один из них не обеспечивает элегантного способа сделать это в ASP.NET MVC.

Эта ссылка - Как заставить браузер перезагружать кэшированные файлы CSS/JS? - предоставляет решение для 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>

Спасибо

4 ответа

Решение

Столкнувшись с этой проблемой, я написал ряд функций-оберток вокруг UrlHelper"s 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>

В этой статье обсуждается, как создавать правила перезаписи на IIS7.

Этот код использует номер версии текущей сборки в качестве параметра строки запроса в пути к файлу, который она испускает. Когда я делаю обновление сайта и увеличивает номер сборки, то же самое происходит с параметром querystring для файла, и поэтому пользовательский агент перезагружает файл.

ОБНОВЛЕНИЕ: Предыдущая версия не работала на Azure, я упростил и исправил ниже. (Обратите внимание, что для работы в режиме разработки с IIS Express вам потребуется установить URL Rewrite 2.0 от Microsoft http://www.iis.net/downloads/microsoft/url-rewrite - он использует установщик WebPi, убедитесь, что закрыть Visual Studio первым)

ОБНОВЛЕНИЕ: Исправлено правило для файлов.min

Недавно я провел совершенно бесплодный день, пытаясь заставить работать автоматическое связывание (для поддержки автоматического управления версиями) в C# / Net 4.6 / MVC 5 / Razor. Я прочитал много статей, как в Stackru, так и в других местах, но не смог найти подробного описания того, как его настроить. Мне также не важно, как файлы редактируются версией (добавляя строку запроса с версией к статическому запросу к файлу - т.е. somefile.js? V =1234), потому что некоторые говорят мне, что некоторые прокси-серверы игнорируют запрос строки при кэшировании статических ресурсов.

Итак, после небольшого путешествия по кроличьей норе, я свернул свою собственную версию для автоматического управления версиями и включил полные инструкции о том, как заставить ее работать ниже.

Полное обсуждение @: упрощенное автоматическое управление версиями Javascript / CSS в ASP.NET MVC 5 для предотвращения проблем с кэшированием (работает в Azure и локально) с перезаписью URL или без нее

ПРОБЛЕМА: у вас обычно есть 2 типа файлов javascript / css в проекте.

1) 3-сторонние библиотеки (такие как jquery или усы), которые очень редко изменяются (и когда они изменяются, версия файла обычно изменяется) - их можно связывать / минимизировать по мере необходимости с помощью WebGrease или JSCompress.com (просто включите связанный файл / версию в ваш _Layout.cshtml)

2) специфичные для страницы файлы css/js, которые должны обновляться всякий раз, когда создается новая сборка. (без необходимости очищать свой кеш или делать несколько обновлений)

Мое решение: автоматически увеличивать версию сборки при каждом построении проекта и использовать это число для перенаправленного статического файла на определенных ресурсах, которые вы хотели бы обновлять. (то есть что-то.js включено в файл something.v1234.js с автоматическим изменением 1234 при каждом построении проекта) - я также добавил некоторые дополнительные функции, чтобы гарантировать, что файлы.min.js используются в рабочей среде и используются файлы обычных файлов.js при отладке (я использую WebGrease для автоматизации процесса минимизации) Одна из приятных особенностей этого решения заключается в том, что оно работает как в локальном / dev-режиме, так и в рабочей среде.

Как это сделать: автоматически увеличивать версию сборки при каждой сборке проекта и использовать это число для перенаправленного статического файла на конкретных ресурсах, которые вы хотите обновлять. (то есть что-то.js включено в файл something.v1234.js с автоматическим изменением 1234 при каждом построении проекта) - я также добавил некоторые дополнительные функции, чтобы гарантировать, что файлы.min.js используются в рабочей среде и используются файлы обычных файлов.js при отладке (я использую WebGrease для автоматизации процесса минимизации) Одна из приятных особенностей этого решения заключается в том, что оно работает как в локальном / dev-режиме, так и в рабочей среде. (Я использую 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: Настройте перезапись URL-адреса в файле web.config для файлов со слагами встроенной версии (см. Шаг 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.

в 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: Измените ссылки src в представлениях Razor, используя переменные приложения, которые мы установили в Global.asax.cs

@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. Если я добавляю или изменяю какие-либо файлы в комплекте (что происходит редко), я вручную переименовываю файл в all3bnd101.min.js, all3bnd102.min.js и т. Д. Этот файл не соответствует обработчику перезаписи, поэтому будет храниться в кэше в клиентском браузере, пока вы не перепутаете / не меняете имя вручную.

Второй файл - это ui.js (который будет записан как ui.v12345123.js или ui.v12345123.min.js в зависимости от того, работаете вы в режиме отладки или нет). Он будет обработан / переписан. (вы можете установить точку останова в Application_OnBeginRequest из Global.asax.cs, чтобы посмотреть, как она работает)

Полное обсуждение этого вопроса: Упрощенное автоматическое управление версиями Javascript / CSS в ASP.NET MVC 5, чтобы остановить проблемы с кэшированием (работает в Azure и локально) с перезаписью URL или без нее (включая способ сделать это БЕЗ перезаписи URL)

Я обычно добавляю поддельную строку запроса в мои файлы ресурсов.. т.е.

<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= вручную, но было бы очень сложно добавить параметр версии к ресурсам из файла конфигурации где-нибудь.

Редактировать:

Я вернулся и тщательно прочитал содержание ссылки выше и понял, что вы, вероятно, уже отказались от этого метода. Извиняюсь за предложение снова.

Я полагаю, следующее решение с расширенными возможностями (режим отладки / выпуска, версии):

Файлы 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();
    }
    ...
}
Другие вопросы по тегам