Автоверсионный CSS/JS в ASP.NET MVC?
Поэтому я читал эту статью об "стековерсии" в ASP.NET MVC для файлов CSS/JS и интересовался, какова "лучшая" стратегия для этого.
Предоставленное решение вставляет номер сборки - что означает каждый раз, когда вы публикуете - это изменит КАЖДЫЙ ОДИН файл, который не идеален, потому что если вы вносите изменения только в 1 *.css или *.js, то он будет изменять каждый файл.
1) Как это можно сделать только для "отдельных файлов" вместо использования всей сборки сайта с использованием даты модификации или чего-то еще в IIS7?
2) Также, если у меня есть какой-то "статический" ресурс, такой как - http://static.domain.com/js/123.js - как я могу использовать перезапись для отправки последнего файла для запроса, если кто-то интегрировал эту статическую ссылка на их сайт?
т. е. http://static.domain.com/js/123.js - это ссылка, и когда для этого приходит запрос - проверить и отправить последний файл?
6 ответов
Я решил эту проблему, добавив автоверсионность в свой проект MVC в файле AssemblyInfo.cs следующим образом:
Change
[assembly: AssemblyVersion("1.0.0.0")]
to
[assembly: AssemblyVersion("1.0.*")]
Это означает, что каждый раз, когда проект собирается, у него будет новая версия сборки, которая выше, чем предыдущая. Теперь у вас есть уникальный номер версии.
Затем я создал класс UrlHelperExtension, который поможет мне получить эту информацию, когда она мне понадобится в моих представлениях:
public static class UrlHelperExtensions
{
public static string ContentVersioned(this UrlHelper self, string contentPath)
{
string versionedContentPath = contentPath + "?v=" + Assembly.GetAssembly(typeof(UrlHelperExtensions)).GetName().Version.ToString();
return self.Content(versionedContentPath);
}
}
Теперь вы можете легко добавить номер версии в свои представления следующим образом:
<link href="@Url.ContentVersioned("style.css")" rel="stylesheet" type="text/css" />
При просмотре исходной страницы у вас теперь будет что-то похожее
<link href="style.css?v=1.0.4809.30029" rel="stylesheet" type="text/css" />
ОБНОВЛЕНИЕ: Предыдущая версия не работала на Azure, я упростил и исправил ниже. (Обратите внимание, что для работы в режиме разработки с IIS Express вам потребуется установить URL Rewrite 2.0 от Microsoft http://www.iis.net/downloads/microsoft/url-rewrite - он использует установщик WebPi, убедитесь, что закрыть Visual Studio первым)
Если вы хотите изменить фактические имена файлов, а не добавлять строку запроса (которая игнорируется некоторыми прокси-серверами / браузерами для статических файлов) Вы можете выполнить следующие шаги: (Я знаю, что это старая запись, но я запустил через это при разработке решения:
Как это сделать: автоматически увеличивать версию сборки при каждой сборке проекта и использовать это число для перенаправленного статического файла на конкретных ресурсах, которые вы хотите обновлять. (то есть что-то.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>
раздел я положил его сразу после </httpProtocol>
конец тега.
<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)
1) Вместо этого используйте время изменения файла. Вот пример:
public static string GeneratePathWithTime(string cssFileName)
{
var serverFilePath = server.MapPath("~/static/" + cssFileName);
var version = File.GetLastWriteTime(serverFilePath).ToString("yyyyMMddhhmmss");
return string.Format("/static/{0}/{1}", version, cssFileName);
}
Это сгенерирует путь как/static/201109231100/style.css
" за "style.css
"(при условии, что ваш style.css
находится в static
каталог). Затем вы добавите правило перезаписи в IIS, чтобы переписать "/static/201109231100/style.css
"до"/static/style.css
Msgstr "Номер версии будет изменен только после изменения файла css и применяется только к измененным файлам.
2) Вы можете обработать запрос к 123.js через HttpModule и отправить его последнее содержимое, но я не думаю, что вы можете гарантировать, что запрос получит последнюю версию. Это зависит от того, как браузер обрабатывает свой кеш. Вы можете установить более раннее время истечения (например, одну минуту назад) в своем заголовке ответа, чтобы браузеры всегда загружали файл заново, но сам браузер сам решает, перезагружать файл или нет., Вот почему нам нужно генерировать новый путь для наших измененных файлов каждый раз, когда мы обновляли наши файлы в вашем вопросе 1), браузер всегда будет пытаться загрузить файл, если URL никогда ранее не посещался.
Я написал Url Helper, который делает CacheBusting для меня.
public static string CacheBustedContent(this UrlHelper helper, string contentPath)
{
var path = string.Empty;
if (helper.RequestContext.HttpContext.Cache["static-resource-" + contentPath] == null)
{
var fullpath = helper.RequestContext.HttpContext.Server.MapPath(contentPath);
var md5 = GetMD5HashFromFile(fullpath);
path = helper.Content(contentPath) + "?v=" + md5;
helper.RequestContext.HttpContext.Cache.Add("static-resource-" + contentPath, path, null, System.Web.Caching.Cache.NoAbsoluteExpiration, new TimeSpan(24, 0, 0), System.Web.Caching.CacheItemPriority.Default, null);
}
else
{
path = helper.RequestContext.HttpContext.Cache["static-resource-" + contentPath].ToString();
}
return path;
}
Вы можете заменить GetMD5HashFromFile() на CRC или любой другой вид вызова, который генерирует уникальную строку на основе содержимого или даты последнего изменения файла.
Недостатком является то, что он будет вызываться всякий раз, когда кеш становится недействительным. И если вы как-то измените файл в режиме реального времени, но не сбросите пул приложений, вам, вероятно, потребуется коснуться web.config, чтобы он перезагрузился правильно.
Этот вопрос действительно старый, но если кто-то наткнется на него, вот, насколько мне известно, текущее состояние дел:
В ASP.NET Core вы можете использовать TagHelpers и просто добавить
asp-append-version
приписывать любому<link>
или<script>
ярлык:<script src="~/js/my.js" asp-append-version="true"></script>
Как для ASP.NET Core, так и для Framework существует пакет NuGet под названием WebOptimizer (https://github.com/ligershark/WebOptimizer). Он позволяет объединять и минимизировать, а также добавляет в файл строку версии на основе содержимого.
Если вы хотите сделать это самостоятельно, есть удобный
IFileVersionProvider
интерфейс, который вы можете получить из своегоIServiceProvider
в .NET Core:// this example assumes, you at least have a HttpContext var fileVersionProvider = httpContext.RequestServices.GetRequiredService<IFileVersionProvider>(); string path = httpContext.Content("/css/site.css"); string pathWithVersionString = fileVersionProvider.AddFileVersionToPath(httpContext.Request.PathBase, path);
Для .NET Framework вы можете получить
FileVersionProvider
источник отсюда: https://github.com/dotnet/aspnetcore/blob/main/src/Mvc/Mvc.Razor/src/Infrastructure/DefaultFileVersionProvider.cs Вам придется проделать некоторую работу, например, заменить кеш наMemoryCache.Default
илиConcurrentDictionary
или что-то в этом роде, но «мясо» есть.
Возможно, вы захотите взглянуть на Blogpost MVC Дина Хьюма и кэш приложений HTML5. В этом посте он указывает на элегантный способ автоматической обработки версий для каждого запроса с использованием библиотеки классов @ShirtlessKirk:
@Url.Content("~/Content/Site.css").AppendHash(Request)