Problemi con i CSS
Dal riferimenti CSS URL relativi per le immagini che potrebbero cambiare pure, e sarà necessario per calcolare il calcolo dell'hash molto prima di avviare l'app che rallenterà la generazione dell'URL della firma. Risulta che scrivere codice per tenere traccia della data dell'ultima modifica non funziona con gli URL delle immagini CSS. Quindi, se una qualsiasi delle immagini riferite all'interno di css cambia, anche il css deve essere cambiato.
problemi relativi alle singole versioni dei file come jquery-1.11.1.js
In primo luogo si rompe il codice sorgente delle versioni, Git o qualsiasi controllo di versione identificherà app-script-1.11.js e app-script 1.12 .js come due file diversi, sarà difficile mantenere la cronologia.
Per jquery, funzionerà mentre stanno creando una libreria e molto spesso non lo cambierai mentre includi le risorse sulla tua pagina, ma mentre crei un'applicazione, avremo molti file JavaScript e la versione che cambia richiederà la modifica di ogni pagina, tuttavia, il singolo file include potrebbe farlo, ma considera un sacco di css e molte immagini.
cache Ultimo aggiornamento come Prefisso URL
così abbiamo dovuto venire con il controllo delle versioni del contenuto statico come /cached/lastupdate/
, questo non è altro che solo un prefisso URL per la risorsa statica. lastupdate
non è altro che l'ultima volta data-ora aggiornata del file richiesto. C'è anche un watcher che aggiorna la chiave di cache se il file viene modificato durante l'ambito dell'applicazione.
Uno degli approcci più semplici è l'utilizzo di una chiave di versione nell'URL.
Definire versione impostazioni di app come seguire
<appSettings>
<add key="CDNHost" value="cdn1111.cloudfront.net"/>
</appSettings>
// Route configuration
// set CDN if you have
string cdnHost = WebConfigrationManager.AppSettings["CDNHost"];
if(!string.IsEmpty(cdnHost)){
CachedRoute.CDNHost = cdnHost;
}
// get assembly build information
string version = typeof(RouteConfig).Assembly.GetName().Version.ToString();
CachedRoute.CORSOrigins = "*";
CachedRoute.Register(routes, TimeSpam.FromDays(30), version);
Ora su ogni pagina, fare riferimento sul suo sito web statico,
<script src="@CachedRoute.CachedUrl("/scripts/jquery-1.11.1.js")"></script>
mentre il rendering, la pagina sarà reso come (senza CDN)
<script src="/cached/2015-12-12-10-10-10-1111/scripts/jquery-1.11.1.js"></script>
Con CDN come
<script
src="//cdn111.cloudfront.net/cached/2015-12-12-10-10-10-1111/scripts/jquery-1.11.1.js">
</script>
Inserire la versione nel percorso URL anziché nella stringa di query rende il rendimento di CDN migliore in quanto le stringhe di query possono essere ignorate nella configurazione CDN (che di solito è il caso predefinito).
CachedRoute classe da https://github.com/neurospeech/atoms-mvc.net/blob/master/src/Mvc/CachedRoute.cs
public class CachedRoute : HttpTaskAsyncHandler, IRouteHandler
{
private CachedRoute()
{
// only one per app..
}
private string Prefix { get; set; }
public static string Version { get; private set; }
private TimeSpan MaxAge { get; set; }
public static string CORSOrigins { get; set; }
//private static CachedRoute Instance;
public static void Register(
RouteCollection routes,
TimeSpan? maxAge = null,
string version = null)
{
CachedRoute sc = new CachedRoute();
sc.MaxAge = maxAge == null ? TimeSpan.FromDays(30) : maxAge.Value;
if (string.IsNullOrWhiteSpace(version))
{
version = WebConfigurationManager.AppSettings["Static-Content-Version"];
if (string.IsNullOrWhiteSpace(version))
{
version = Assembly.GetCallingAssembly().GetName().Version.ToString();
}
}
Version = version;
var route = new Route("cached/{version}/{*name}", sc);
route.Defaults = new RouteValueDictionary();
route.Defaults["version"] = "1";
routes.Add(route);
}
public override bool IsReusable
{
get
{
return true;
}
}
public static string CDNHost { get; set; }
public override bool IsReusable
{
get
{
return true;
}
}
public class CachedFileInfo
{
public string Version { get; set; }
public string FilePath { get; set; }
public CachedFileInfo(string path)
{
path = HttpContext.Current.Server.MapPath(path);
FilePath = path;
//Watch();
Update(null, null);
}
private void Watch()
{
System.IO.FileSystemWatcher fs = new FileSystemWatcher(FilePath);
fs.Changed += Update;
fs.Deleted += Update;
fs.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.Size | NotifyFilters.FileName;
}
private void Update(object sender, FileSystemEventArgs e)
{
FileInfo f = new FileInfo(FilePath);
if (f.Exists)
{
Version = f.LastWriteTimeUtc.ToString("yyyy-MM-dd-hh-mm-ss-FFFF");
}
else
{
Version = "null";
}
}
}
private static ConcurrentDictionary<string, CachedFileInfo> CacheItems = new ConcurrentDictionary<string, CachedFileInfo>();
public static HtmlString CachedUrl(string p)
{
//if (!Enabled)
// return new HtmlString(p);
if (!p.StartsWith("/"))
throw new InvalidOperationException("Please provide full path starting with /");
string v = Version;
var cv = CacheItems.GetOrAdd(p, k => new CachedFileInfo(k));
v = cv.Version;
if (CDNHost != null)
{
return new HtmlString("//" + CDNHost + "/cached/" + v + p);
}
return new HtmlString("/cached/" + v + p);
}
public override async Task ProcessRequestAsync(HttpContext context)
{
var Response = context.Response;
Response.Cache.SetCacheability(HttpCacheability.Public);
Response.Cache.SetMaxAge(MaxAge);
Response.Cache.SetExpires(DateTime.Now.Add(MaxAge));
if (CORSOrigins != null)
{
Response.Headers.Add("Access-Control-Allow-Origin", CORSOrigins);
}
string FilePath = context.Items["FilePath"] as string;
var file = new FileInfo(context.Server.MapPath("/" + FilePath));
if (!file.Exists)
{
throw new FileNotFoundException(file.FullName);
}
Response.ContentType = MimeMapping.GetMimeMapping(file.FullName);
using (var fs = file.OpenRead())
{
await fs.CopyToAsync(Response.OutputStream);
}
}
IHttpHandler IRouteHandler.GetHttpHandler(RequestContext requestContext)
{
//FilePath = requestContext.RouteData.GetRequiredString("name");
requestContext.HttpContext.Items["FilePath"] = requestContext.RouteData.GetRequiredString("name");
return (IHttpHandler)this;
}
}
Utilizzando file di modifica Ora invece della versione
public static HtmlString CachedUrl(string p)
{
if (!p.StartsWith("/"))
throw new InvalidOperationException("Please provide full path starting with /");
var ft = (new System.IO.FileInfo(Server.MapPath(p)).LastModified;
return new HtmlString(cdnPrefix + "/cached/" + ft.Ticks + p);
}
Questa versione mantiene sulla base di ultima modifica, ma questo aumenta chiamata a System.IO.FileInfo
a ogni richiesta, tuttavia è possibile creare un altro dizionario per memorizzare queste informazioni e osservare i cambiamenti, ma è molto lavoro.
cos'è un "URL di impronte digitali"? – dandavis
@dandavis: è una tecnica che invalida i contenuti non aggiornati quando si utilizzano [caching aggressivo] (https://developers.google.com/speed/docs/insights/LeverageBrowserCaching). Questo comportamento (busting della cache) viene in genere eseguito facendo riferimento alla versione del file o all'hash "fingerprint" nell'URL i.e. app.js? V = 123'; ogni volta che viene rilasciato un aggiornamento, il file viene fornito da un URL diverso. –
come visto dal client, tutti gli URL sono codificati nell'html come attributi come src e href? – dandavis