-
12.08.2019, 21:35 #1
- Registriert seit
- 15.11.2011
- Beiträge
- 6.262
- Blog Entries
- 5
Thanked 9.145 Times in 3.019 PostsCaching Buster einer MVC-Route auf Basis einer Razor-View in ASP.NET Core
ASP.NET Core besitzt in ihren TagHelpern einen coolen Parameter namens asp-append-version. Wenn man den auf true setzt, wird die Datei gehasht und automatisch als URL-Parameter angehangen. Dies verhindert das Cachen im Browser (gut, außer IE, aber das ist ja eigentlich auch kein Browser...) - Das ganze Arbeitet mit Caching auf Basis eines FileWatchers, also in Echtzeit und extrem performant. Für JS Dateien im Dateisystem super, da nutze ich das standardmäßig.
Genau das will ich haben, aber für eine dynamisch generierte Razor-View. Die Ansicht gibt JavaScript-Code zurück, der dynamisch generierte Konfigurationsparameter enthält. Die Action dazu sieht wie folgt aus:
public IActionResult Config() {
Response.ContentType = "text/javascript";
string ExpireDate = DateTime.UtcNow.AddMonths(2).ToString("ddd, dd MMM yyyy HH:mm:ss", CultureInfo.InvariantCulture);
Response.Headers.Add("Expires", $"{ExpireDate} GMT");
return View("~/Views/Home/Config.cshtml");
}
In .NET Core 2.2 ist eine Erweiterung AddFileVersionToPath() enthalten, die quasi das asp-append-version Attribut auf Dateien direkt anwendet. Sprich ohne den TagHelper. Da ich die LTS (2.1) verwende und das schon für einen Editor gebraucht habe, der via JS-Variable ein Stylesheet nachlädt, habe ich folgendes Polyfill:
public static class RazorExtensions {
/// <summary>
/// Gets the path to a file in wwwroot with version hash for cache management
/// </summary>
public static string AddFileVersionToPath(this IRazorPage page, string path) {
var context = page.ViewContext.HttpContext;
IMemoryCache cache = context.RequestServices.GetRequiredService<IMemoryCache>();
var hostingEnvironment = context.RequestServices.GetRequiredService<IHostingEnvironment>();
var versionProvider = new FileVersionProvider(hostingEnvironment.WebRootFileProvider, cache, context.Request.Path);
return versionProvider.AddFileVersionToPath(path);
}
}
Problem ist, dass dabei meine URLs kaputt gehen. Ich habe eben ja nicht einen einzigen Pfad, der im wwwroot liegt und an den er einfach seinen Cache-Buster anhängen soll wie /css/foo.css. Sondern er soll /Views/Home/Config.cshtml.js als Grundlage für den Hash nutzen und diesen dann aber an die Route /home/config anhängen.
@{
string configPath = this.AddFileVersionToPath("../Views/Home/Config.cshtml");
}
Da er als Basis den WebRoot nutzt, müsste das logisch richtig sein. Er ermittelt aber nicht mal den Hash. Und wie schon eben erwähnt, generiert er mir einen JS Tag mit ../Views/Home/Config.cshtml.js der so natürlich auch nicht als Route existiert.
Jemand eine Idee, wie man das fix lösen kann?
Spontan fällt mir da nichts ein außer eben den FileVersionProvider genauer anzuschauen und den ein Stück weit angepasst nachzubauen, sodass zwischen zu hashender Datei und der Zieldatei unterschieden werden kann.
-
13.08.2019, 19:30 #2
- Registriert seit
- 15.11.2011
- Beiträge
- 6.262
- Blog Entries
- 5
Thanked 9.145 Times in 3.019 PostsAW: Caching Buster einer MVC-Route auf Basis einer Razor-View in ASP.NET Core
Hab mir selbst eine entsprechende Erweiterung gebastelt:
public static class RazorExtensions {
/// <summary>
/// Gets the path to a virtual file (e.g. MVC route) with version hash, where the hash is calculated from a dynamic Razor file. Caches the hash using a file watcher.
/// IMPORTANT: To make this work, set <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> for the affected view to avoid missing file exceptions when deploying to prod server without VS dev env
/// </summary>
/// <param name="physicalRazorFilePath">Path to an existing file relative to the project root dir (e.g. /Views/Dummy/SomeView.cshtml)</param>
/// <param name="virtualWebPath">Route where the file would be accessed (e.g. /Dummy/SomeView) - This is used as base for the caching parameter</param>
/// <returns></returns>
public static string AddFileVersionToVirtualPath(this IRazorPage page, string physicalRazorFilePath, string virtualWebPath, string noCacheKey = "v") {
var context = page.ViewContext.HttpContext;
IMemoryCache cache = context.RequestServices.GetRequiredService<IMemoryCache>();
string cacheKey = physicalRazorFilePath + virtualWebPath;
if(cache.TryGetValue(cacheKey, out string value)) {
return value;
}
var hostingEnvironment = context.RequestServices.GetRequiredService<IHostingEnvironment>();
var fileInfo = hostingEnvironment.ContentRootFileProvider.GetFileInfo(physicalRazorFilePath);
string hash = "";
using (var stream = fileInfo.CreateReadStream()) {
var ms = new MemoryStream();
stream.CopyTo(ms);
byte[] data = ms.ToArray();
var algorithm = SHA256.Create();
var sbHash = new StringBuilder();
byte[] rawHash = algorithm.ComputeHash(data);
foreach(byte b in rawHash) {
sbHash.Append(b.ToString("x2"));
}
hash = sbHash.ToString();
}
string fullPath = QueryHelpers.AddQueryString(virtualWebPath, noCacheKey, hash);
var token = hostingEnvironment.ContentRootFileProvider.Watch(physicalRazorFilePath);
var cacheEntryOptions = new MemoryCacheEntryOptions().AddExpirationToken(token);
cache.Set(cacheKey, fullPath, cacheEntryOptions);
return fullPath;
}
}
Beispiel:
@{
string configPath = this.AddFileVersionToVirtualPath("/Views/Home/Config.cshtml", "/home/config");
}
<script src="@configPath"></script>
Ergibt folgendes gerendertes HTML:
HTML-Code:<script src="/home/config?v=783d8bfbcf1a33ad0c46b40fbe456350372b2e0cc0b72b2201353445b01016e4"></script>
Ähnliche Themen
-
[Vorstellung] einer mehr im Gewimmel
Von Babaji im Forum UserankündigungenAntworten: 6Letzter Beitrag: 27.06.2013, 17:31 -
Kauf einer xBox360
Von Kolle1991 im Forum Microsoft XboxAntworten: 21Letzter Beitrag: 16.04.2012, 14:57 -
Was in einer Internetminute passiert
Von DMW007 im Forum Internet und TechnikAntworten: 3Letzter Beitrag: 03.04.2012, 01:55 -
Wertfrage einer Wii
Von Young Jeezy im Forum NintendoAntworten: 7Letzter Beitrag: 25.02.2012, 23:13
Diese Seite nutzt Cookies, um das Nutzererlebnis zu verbessern. Klicken Sie hier, um das Cookie-Tracking zu deaktivieren.