MVC4 StyleBundle: можете ли вы добавить строку запроса на удаление кеша в режиме отладки?
У меня есть приложение MVC, и я использую класс StyleBundle
для рендеринга CSS файлов следующим образом:
bundles.Add(new StyleBundle("~/bundles/css").Include("~/Content/*.css"));
Проблема заключается в том, что в режиме Debug
URL-адреса CSS выводятся отдельно, и у меня есть веб-прокси, который агрессивно кэширует эти URL-адреса. В режиме Release
я знаю, что строка запроса добавляется к окончательному URL-адресу, чтобы аннулировать любые кеши для каждой версии.
Можно ли настроить StyleBundle
для добавления случайной последовательности в режиме Debug
, а также для создания следующего вывода, чтобы обойти проблему кэширования?
<link href="/stylesheet.css?random=some_random_string" rel="stylesheet"/>
Ответы
Ответ 1
Для этого вы можете создать собственный класс IBundleTransform. Вот пример, который добавит параметр v = [filehash], используя хэш содержимого файла.
public class FileHashVersionBundleTransform: IBundleTransform
{
public void Process(BundleContext context, BundleResponse response)
{
foreach(var file in response.Files)
{
using(FileStream fs = File.OpenRead(HostingEnvironment.MapPath(file.IncludedVirtualPath)))
{
//get hash of file contents
byte[] fileHash = new SHA256Managed().ComputeHash(fs);
//encode file hash as a query string param
string version = HttpServerUtility.UrlTokenEncode(fileHash);
file.IncludedVirtualPath = string.Concat(file.IncludedVirtualPath, "?v=", version);
}
}
}
}
Затем вы можете зарегистрировать класс, добавив его в коллекцию Transforms ваших пакетов.
new StyleBundle("...").Transforms.Add(new FileHashVersionBundleTransform());
Теперь номер версии изменится только при изменении содержимого файла.
Ответ 2
Вам просто нужна уникальная строка. Это не должно быть Хэшем. Мы используем дату LastModified файла и получаем Ticks оттуда. Открытие и чтение файла дорого, как отметил @Todd. Ticks достаточно для вывода уникального числа, которое изменяется при изменении файла.
internal static class BundleExtensions
{
public static Bundle WithLastModifiedToken(this Bundle sb)
{
sb.Transforms.Add(new LastModifiedBundleTransform());
return sb;
}
public class LastModifiedBundleTransform : IBundleTransform
{
public void Process(BundleContext context, BundleResponse response)
{
foreach (var file in response.Files)
{
var lastWrite = File.GetLastWriteTime(HostingEnvironment.MapPath(file.IncludedVirtualPath)).Ticks.ToString();
file.IncludedVirtualPath = string.Concat(file.IncludedVirtualPath, "?v=", lastWrite);
}
}
}
}
и как его использовать:
bundles.Add(new StyleBundle("~/bundles/css")
.Include("~/Content/*.css")
.WithLastModifiedToken());
и это то, что пишет MVC:
<link href="bundles/css/site.css?v=635983900813469054" rel="stylesheet"/>
отлично работает с пакетами Script.
Ответ 3
Эта библиотека может добавить хэш кеша к вашим файлам пакетов в режиме отладки, а также несколько других вещей, связанных с кэшированием: https://github.com/kemmis/System.Web.Optimization.HashCache p >
Вы можете применить HashCache ко всем пакетам в BundlesCollection
Выполнить метод расширения ApplyHashCache() на экземпляре BundlesCollection
после того, как все пакеты были добавлены в коллекцию.
BundleTable.Bundles.ApplyHashCache();
Или вы можете применить HashCache к одному Bundle
Создайте экземпляр HashCacheTransform и добавьте его в экземпляр пакета, который вы хотите
для применения HashCache к.
var myBundle = new ScriptBundle("~/bundle_virtual_path").Include("~/scripts/jsfile.js");
myBundle.Transforms.Add(new HashCacheTransform());
Ответ 4
У меня была такая же проблема, но с кешированными версиями в клиентских браузерах после обновления. Мое решение состоит в том, чтобы обернуть вызов @Styles.Render("~/Content/css")
в моем собственном рендерере, который добавляет наш номер версии в строку запроса следующим образом:
public static IHtmlString RenderCacheSafe(string path)
{
var html = Styles.Render(path);
var version = VersionHelper.GetVersion();
var stringContent = html.ToString();
// The version should be inserted just before the closing quotation mark of the href attribute.
var versionedHtml = stringContent.Replace("\" rel=", string.Format("?v={0}\" rel=", version));
return new HtmlString(versionedHtml);
}
И тогда в представлении мне это нравится:
@RenderHelpers.RenderCacheSafe("~/Content/css")
Ответ 5
В настоящее время, но это скоро будет добавлено (сейчас запланировано для версии с стабильной версией 1.1, вы можете отслеживать эту проблему здесь: Codeplex
Ответ 6
Обратите внимание, что это написано для скриптов, но также работает для стилей (просто измените эти ключевые слова)
Основываясь на ответе @Johan:
public static IHtmlString RenderBundle(this HtmlHelper htmlHelper, string path)
{
var context = new BundleContext(htmlHelper.ViewContext.HttpContext, BundleTable.Bundles, string.Empty);
var bundle = System.Web.Optimization.BundleTable.Bundles.GetBundleFor(path);
var html = System.Web.Optimization.Scripts.Render(path).ToString();
foreach (var item in bundle.EnumerateFiles(context))
{
if (!html.Contains(item.Name))
continue;
html = html.Replace(item.Name, item.Name + "?" + item.LastWriteTimeUtc.ToString("yyyyMMddHHmmss"));
}
return new HtmlString(html);
}
public static IHtmlString RenderStylesBundle(this HtmlHelper htmlHelper, string path)
{
var context = new BundleContext(htmlHelper.ViewContext.HttpContext, BundleTable.Bundles, string.Empty);
var bundle = System.Web.Optimization.BundleTable.Bundles.GetBundleFor(path);
var html = System.Web.Optimization.Styles.Render(path).ToString();
foreach (var item in bundle.EnumerateFiles(context))
{
if (!html.Contains(item.Name))
continue;
html = html.Replace(item.Name, item.Name + "?" + item.LastWriteTimeUtc.ToString("yyyyMMddHHmmss"));
}
return new HtmlString(html);
}
Использование:
@Html.RenderBundle("...")
@Html.RenderStylesBundle("...")
Замена
@Scripts.Render("...")
@Styles.Render("...")
Преимущества:
- Работает для v1.0.0.0 System.Web.Optimizations
- Работает с несколькими файлами в комплекте
- Возвращает дату изменения файла, а не хэширование каждого файла, а не группу
Кроме того, когда вам нужно быстро обходиться с Bundler:
public static MvcHtmlString ResolveUrl(this HtmlHelper htmlHelper, string url)
{
var urlHelper = new UrlHelper(htmlHelper.ViewContext.RequestContext);
var resolvedUrl = urlHelper.Content(url);
if (resolvedUrl.ToLower().EndsWith(".js") || resolvedUrl.ToLower().EndsWith(".css"))
{
var localPath = HostingEnvironment.MapPath(resolvedUrl);
var fileInfo = new FileInfo(localPath);
resolvedUrl += "?" + fileInfo.LastWriteTimeUtc.ToString("yyyyMMddHHmmss");
}
return MvcHtmlString.Create(resolvedUrl);
}
Использование:
<script type="text/javascript" src="@Html.ResolveUrl("~/Scripts/jquery-1.9.1.min.js")"></script>
Замена:
<script type="text/javascript" src="@Url.Content("~/Scripts/jquery-1.9.1.min.js")"></script>
(Также заменяет многие другие альтернативные запросы)