ASP.NET MVC Bundling cache. (Detecting css files changes) (internal behaviour) - css

I've been diving into ASP.NET MVC internal functionality much (different reasons), but still can not cover all the behaviour. One of those which I did not is subj.
The way it works is the following:
if I bundle some files (css files for instance), the framework detects those changes and generates new id for the new bundle (to make it easy for browsers to refresh the changes) like href="/Content/css?v=qartPE4jGe-l1U0I7kNDZPZzVTdh0kT8VBZZA_uURjI1".
What I am actually trying to understand:
How exactly the framework (that's possibly not MVC but .NET stuff) detects that the files are changed (as there are no directory watchers active (as I can change the file even when web-server if off-line) to see the file changes live, and also the system detects actually the file content changes (I tried just to re-save files without changing their contents and the bundle number did not change as well))?
(I consider that obviously the system can not compare every file content to detect its changings on every request came).
Where (and how) the frameworks stores current bundle id and how it stores previous versions (as previous bundles are still available when go to their urls)?
Thanks a lot!

The ASP.NET Optimization framework caches the bundle response in HttpContext.Cache and uses a CacheDependency to monitor each file in the bundle for changes. This is why updating the files directly invalidates the cache and regenerates the bundle.
The bundle file name is a hash of the bundle contents which ensures the URL changes when any of the bundle files are modified. The bundle's virtual path is used as the cache key.
The relevant code from the library (note this is slightly out of date but I believe the logic is still the same):
internal BundleResponse GetBundleResponse(BundleContext context)
{
// check to see if the bundle response is in the cache
BundleResponse bundleResponse = Bundle.CacheLookup(context);
if (bundleResponse == null || context.EnableInstrumentation)
{
// if not, generate the bundle response and cache it
bundleResponse = this.GenerateBundleResponse(context);
if (context.UseServerCache)
{
this.UpdateCache(context, bundleResponse);
}
}
return bundleResponse;
}
private void UpdateCache(BundleContext context, BundleResponse response)
{
if (context.UseServerCache)
{
// create a list of all the file paths in the bundle
List<string> list = new List<string>();
list.AddRange(
from f in response.Files
select f.FullName);
list.AddRange(context.CacheDependencyDirectories);
string cacheKey = Bundle.GetCacheKey(context.BundleVirtualPath);
// insert the response into the cache with a cache dependency that monitors
// the bundle files for changes
context.HttpContext.Cache.Insert(cacheKey, response, new CacheDependency(list.ToArray()));
context.HttpContext.Response.AddCacheItemDependency(cacheKey);
this._cacheKeys.Add(cacheKey);
}
}
Finally as for old bundle URLs working, I think you will find they are either returned from your browser cache or actually return the latest version of the bundle since the bundle path doesn't change, only the version query string.

Related

Can I serve static content with spring-boot-starter-tomcat but without spring-boot-starter-web?

I'm working on a web service using spring-boot-starter-jersey and spring-boot-starter-tomcat (v. 1.5.2) and as such, I'd rather not add spring-boot-starter-web and further complicate my configuration. I want to stick the Swagger UI static files in a folder and serve them from there.
What I'm wondering is, can I serve static files using just spring-boot-starter-tomcat? I've found Spring documentation saying that I can server static content from a variety of sources on the classpath, but the examples I've found seem to require Spring MVC. I've tried disabling Jersey and putting static files in src/main/resources/static to test just Tomcat, but when I go to localhost/index.html, I get a 404 not found error.
As you might be able to tell from my path, I'm using Maven.
Since you can serve static files with just Tomcat, it seems like I should be able to serve static files with spring-boot-starter-tomcat. If this is the case, where do I put those files?
To put this another way, say I have started with the Spring-provided
spring-boot-sample-jersey project. I have a requirement that the Jersey web service answer calls to the root address (/). How would I add some static content (HTML, CSS, JS) to be served from subdirectory called /swagger?
So the default servlet (which serves static content) is by default registered. But it will use only search specific paths as the document root. I had to go digging through source code to finally find it. If you look in the AbstractEmbeddedServletContainerFactory, you'll see
private static final String[] COMMON_DOC_ROOTS = {
"src/main/webapp", "public", "static" };
If we don't explicitly set the document root, the above three are the paths that will be searched. In order, the first directory found that exists, will be used as the document root. I've verified that all of these work.
If you want to set a different directory, you can use a customizer bean
#Bean
public EmbeddedServletContainerCustomizer tomcatCustomizer() {
return new EmbeddedServletContainerCustomizer() {
#Override
public void customize(ConfigurableEmbeddedServletContainer container) {
container.setDocumentRoot(new File("src/main/resources/static"));
}
};
}
The one thing I haven't figured out is if we can serve files as classpath resources, instead of file system resources. If you look at the source code I linked to, it has some code that looks for the existence of a META-INF/resources. I thought that might work, but unfortunately it didn't for me. Maybe some guys of the Spring Boot team can enlighten us.

Symfony 2 reload translation cache

I'm using Symfony2 (soon 3), and we got some translations that are stored in the Database.
This means that when we run cache:clear the translations are fetched from the database and stored in the cache (on disk).
This also means users can change the translations directly in the database, but those changes aren't visible immediately.
Is there a way to only clear the translation cache files in Symfony? Without refreshing the whole cache?
I do this so
$cacheDir = dirname($this->getParameter('kernel.cache_dir'));
foreach (['prod', 'dev'] as $env) {
array_map('unlink', glob("$cacheDir/$env/translations/*"));
}
Just to complete answers already provided, removing the translations folder in cache/ won't be enough if you need to take new translation files into account.
For that, you'll need to also remove the file caching the path of all ressources and asset file.
Its name depends on the environment: appDevDebugProjectContainer.php or appProdProjectContainer.php.
So based on #Atmarama answer:
$cacheDir = dirname($this->getParameter('kernel.cache_dir'));
foreach (['prod', 'dev'] as $env) {
array_map('unlink', glob("$cacheDir/$env/translations/*"));
array_map('unlink', glob("$cacheDir/$env/app*ProjectContainer.php"));
}
Tested with Symfony 3.3.x.
Running ./bin/console cache:clear does not provide any options as to which cache folder should be removed. It just clears the complete folder and only handling warmup is configurable using options. For reference see the CacheClearCommand in the FrameworkBundle.
You could write your own command though that only checks for the translation-cache dir and clears it. By default it resides in a subfolder translations/ which should be easily accessible with a finder like this:
$finder = (new Finder)->in($cacheDir.'/'.$env)->directories()->name('translations');
foreach ($finder as $translationsDir) {
rmdir($translationDir->getPathname();
}

Offline Caching(HTML5)

I am facing some difficulty to update cache information from the server by using HTML 5 offline caching method.
Here is the list of steps,
1- Created one cache.manifest file with following entries
CACHE MANIFEST
# Version 1.0
CACHE:
/loading.js
/images/pan-icon.png
NETWORK:
*
Then i jest add following event binder to load updated information from the server
window.addEventListener('load', function (e) {
window.applicationCache.update();
window.applicationCache.addEventListener('updateready', function (e) {
if (window.applicationCache.status == window.applicationCache.UPDATEREADY) {
// Browser downloaded a new app cache.
window.applicationCache.swapCache();
console.log('Updated');
} else {
console.log('No Update');
}
}, false);
}, false);
But it alwayes failed to get latest 'loading.js' from the server. I need to clear the cache to get update from the server.
Is there any way to update this forefully.
Please help me
I am using ASP .NET MVC framework to build my web application
When ever you change the content of a file that is defined in your manifest, you must update the manifest file as well in order for the browser to pull down the latest content of that file.
From the gotchas section from MDN (https://developer.mozilla.org/en-US/docs/Web/HTML/Using_the_application_cache)
When applications are cached, simply updating the resources (files) that are used in a web page is not enough to update the files that have been cached. You must update the cache manifest file itself before the browser retrieves and uses the updated files. You can do this programmatically using window.applicationCache.swapCache(), though resources that have already been loaded will not be affected. To make sure that resources are loaded from a new version of the application cache, refreshing the page is ideal.

Durandal Caching Issue / Versioning Strategy

What's a good strategy for versioning Durandal js and html files?
I noticed that, during development, your browser cache must be disabled in order for you to receive up to date files on each refresh. This is a must for during development.
However, my concern is that when I go to production with my continuous deployment strategy (deploying multiple times per day), that users' browsers will be caching older versions of my app which might lead to unpredictable behaviour.
The approach that springs to mind would be to version the js and html urls somehow so that there is a version number embedded into every request. But I am unsure as to how to make that work internally within the Durandal framework.
Ok, here is the direction that I am heading in. Basically there is something built into requirejs to handle this.
At the top of my main.js, in the call to requirejs.config I can set a urlArgs property that will be appended to every call requirejs makes for a module.
requirejs.config({
paths: {
'text': 'durandal/amd/text'
},
urlArgs: 'v=1.0.0.0'
});
When I want to force production users to get a new version of the requirejs modules I can just increment the version number which will invalidate the browsers cache.
(In my project I have a way of injecting the version number of the assembly containing my main ASP.NET MVC assembly into this property, but the code for that would have distracted from the simplicity of the above example).
Hope this helps someone!
For .NET, add the main-built.js file as a script bundle in App_Start/BundleConfig:
public static void RegisterBundles(BundleCollection bundles)
{
//...
bundles.Add(new ScriptBundle("~/Scripts/main-built").Include(
"~/App/main-built.js"));
//...
}
Reference the script bundle on your index page:
#if (HttpContext.Current.IsDebuggingEnabled)
{
<script type="text/javascript" src="~/Scripts/require.js" data-main="App/main"></script>
}
else
{
<!-- Remember to run the weyland optimizer to create the main-built.js -->
#Scripts.Render("~/Scripts/main-built")
}
As long as you have the default Web.Release.Config file, Visual Studio will automatically remove debug attributes while also minifying and versioning your bundles upon publishing.

C# Web.Optimization Bundles and HTML5 cache Manifest

I am using ASP.NET optimization package to minify and bundle the scripts, and CSS files.
I am also developing a mobile UI for my ASP.NET application which uses a HTML5 cache manifest.
The optimization package updates the version of the dynamic bundle URL when the files change and the application cache is recycled.
I would like to be able to update my manifest version whenever this happens and include the dynamic URLs the optimization package provides in the manifest.
How can I read the current version (the "v" parameter) or anything else to trigger a manifest update?
/_assets/bundles/global?v=fmbQlO0mGjXyliVEBImQIr5yoMX0Tw0tlMK45jlwHZ81
Example Code:
string version= "2.6";
StringBuilder output = new StringBuilder();
output.AppendLine("CACHE MANIFEST");
output.AppendLine(string.Format("# v{0}", ??????));
output.AppendLine("CACHE:");
output.AppendLine(Scripts.Url("~/bundles/global").ToString());
...
The Application Manifest will automatically trigger an update if it is changed.
With static assets, people usually changed a version number in a comment so that the file was changed and would trigger an update, even though the content under the CACHE, NETWORK and FALLBACK sections were unchanged.
When you are using the URLs generated by System.Web.Optimization, the URL will change when the content of any of the CSS or JavaScript files in the bundles changes. This means that the manifest file will automatically be different to the previous version of the file and will trigger an update.
There is no need to force the file to be different by updating a version comment.

Resources